Check seccomp enable and add unit test for seccomp/apparmor.
Signed-off-by: Lantao Liu <lantaol@google.com>
This commit is contained in:
parent
c83af81403
commit
21233b22be
2
Makefile
2
Makefile
@ -24,7 +24,7 @@ VERSION := $(shell git describe --tags --dirty)
|
|||||||
# strip the first char of the tag if it's a `v`
|
# strip the first char of the tag if it's a `v`
|
||||||
VERSION := $(VERSION:v%=%)
|
VERSION := $(VERSION:v%=%)
|
||||||
TARBALL := cri-containerd-$(VERSION).tar.gz
|
TARBALL := cri-containerd-$(VERSION).tar.gz
|
||||||
BUILD_TAGS := apparmor
|
BUILD_TAGS := seccomp apparmor
|
||||||
GO_LDFLAGS := -X $(PROJECT)/pkg/version.criContainerdVersion=$(VERSION)
|
GO_LDFLAGS := -X $(PROJECT)/pkg/version.criContainerdVersion=$(VERSION)
|
||||||
SOURCES := $(shell find cmd/ pkg/ vendor/ -name '*.go')
|
SOURCES := $(shell find cmd/ pkg/ vendor/ -name '*.go')
|
||||||
INTEGRATION_SOURCES := $(shell find integration/ -name '*.go')
|
INTEGRATION_SOURCES := $(shell find integration/ -name '*.go')
|
||||||
|
@ -32,6 +32,7 @@ import (
|
|||||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
runcapparmor "github.com/opencontainers/runc/libcontainer/apparmor"
|
runcapparmor "github.com/opencontainers/runc/libcontainer/apparmor"
|
||||||
"github.com/opencontainers/runc/libcontainer/devices"
|
"github.com/opencontainers/runc/libcontainer/devices"
|
||||||
|
runcseccomp "github.com/opencontainers/runc/libcontainer/seccomp"
|
||||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/opencontainers/runtime-tools/generate"
|
"github.com/opencontainers/runtime-tools/generate"
|
||||||
"github.com/opencontainers/runtime-tools/validate"
|
"github.com/opencontainers/runtime-tools/validate"
|
||||||
@ -58,12 +59,8 @@ const (
|
|||||||
appArmorDefaultProfileName = "cri-containerd.apparmor.d"
|
appArmorDefaultProfileName = "cri-containerd.apparmor.d"
|
||||||
// unconfinedProfile is a string indicating one should run a pod/containerd without a security profile
|
// unconfinedProfile is a string indicating one should run a pod/containerd without a security profile
|
||||||
unconfinedProfile = "unconfined"
|
unconfinedProfile = "unconfined"
|
||||||
// seccompDefaultSandboxProfile is the default seccomp profile for pods.
|
// seccompDefaultProfile is the default seccomp profile.
|
||||||
seccompDefaultSandboxProfile = dockerDefault
|
seccompDefaultProfile = dockerDefault
|
||||||
// seccompDefaultContainerProfile is the default seccomp profile for containers.
|
|
||||||
seccompDefaultContainerProfile = dockerDefault
|
|
||||||
// seccompEnabled is a flag for globally enabling/disabling seccomp profiles for containers.
|
|
||||||
seccompEnabled = true // TODO (mikebrow): make these seccomp defaults configurable
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -201,59 +198,27 @@ func (c *criContainerdService) CreateContainer(ctx context.Context, r *runtime.C
|
|||||||
if username := securityContext.GetRunAsUsername(); username != "" {
|
if username := securityContext.GetRunAsUsername(); username != "" {
|
||||||
specOpts = append(specOpts, containerd.WithUsername(username))
|
specOpts = append(specOpts, containerd.WithUsername(username))
|
||||||
}
|
}
|
||||||
// Set apparmor profile, (privileged or not) if apparmor is enabled
|
|
||||||
appArmorProf := securityContext.GetApparmorProfile()
|
apparmorSpecOpts, err := generateApparmorSpecOpts(
|
||||||
if !runcapparmor.IsEnabled() {
|
securityContext.GetApparmorProfile(),
|
||||||
// Should fail loudly if user try to specify apparmor profile
|
securityContext.GetPrivileged(),
|
||||||
// but we don't support it.
|
runcapparmor.IsEnabled())
|
||||||
if appArmorProf != "" {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("apparmor is not supported")
|
return nil, fmt.Errorf("failed to generate apparmor spec opts: %v", err)
|
||||||
}
|
}
|
||||||
} else {
|
if apparmorSpecOpts != nil {
|
||||||
switch appArmorProf {
|
specOpts = append(specOpts, apparmorSpecOpts)
|
||||||
case runtimeDefault:
|
|
||||||
// TODO (mikebrow): delete created apparmor default profile
|
|
||||||
specOpts = append(specOpts, apparmor.WithDefaultProfile(appArmorDefaultProfileName))
|
|
||||||
// TODO(random-liu): Should support "unconfined" after kubernetes#52395 lands.
|
|
||||||
case "":
|
|
||||||
// Based on kubernetes#51746, default apparmor profile should be applied
|
|
||||||
// for non-privileged container when apparmor is not specified.
|
|
||||||
if !securityContext.GetPrivileged() {
|
|
||||||
specOpts = append(specOpts, apparmor.WithDefaultProfile(appArmorDefaultProfileName))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// Require and Trim default profile name prefix
|
|
||||||
if !strings.HasPrefix(appArmorProf, profileNamePrefix) {
|
|
||||||
return nil, fmt.Errorf("invalid apparmor profile %q", appArmorProf)
|
|
||||||
}
|
|
||||||
specOpts = append(specOpts, apparmor.WithProfile(strings.TrimPrefix(appArmorProf, profileNamePrefix)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set seccomp profile
|
seccompSpecOpts, err := generateSeccompSpecOpts(
|
||||||
seccompProf := config.GetLinux().GetSecurityContext().GetSeccompProfilePath()
|
securityContext.GetSeccompProfilePath(),
|
||||||
if seccompProf == runtimeDefault || seccompProf == dockerDefault {
|
securityContext.GetPrivileged(),
|
||||||
// use correct default profile (Eg. if not configured otherwise, the default is docker/default for containers)
|
runcseccomp.IsEnabled())
|
||||||
seccompProf = seccompDefaultContainerProfile
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to generate seccomp spec opts: %v", err)
|
||||||
}
|
}
|
||||||
// Unset the seccomp profile, if seccomp is not enabled, unconfined, unset, or the security context is privileged
|
if seccompSpecOpts != nil {
|
||||||
if !seccompEnabled ||
|
specOpts = append(specOpts, seccompSpecOpts)
|
||||||
seccompProf == unconfinedProfile ||
|
|
||||||
seccompProf == "" ||
|
|
||||||
config.GetLinux().GetSecurityContext().GetPrivileged() {
|
|
||||||
spec.Linux.Seccomp = nil
|
|
||||||
} else {
|
|
||||||
switch seccompProf {
|
|
||||||
case dockerDefault:
|
|
||||||
// Note: WithDefaultProfile specOpts must be added after capabilities
|
|
||||||
specOpts = append(specOpts, seccomp.WithDefaultProfile())
|
|
||||||
default:
|
|
||||||
// Require and Trim default profile name prefix
|
|
||||||
if !strings.HasPrefix(seccompProf, profileNamePrefix) {
|
|
||||||
return nil, fmt.Errorf("invalid seccomp profile %q", seccompProf)
|
|
||||||
}
|
|
||||||
specOpts = append(specOpts, seccomp.WithProfile(strings.TrimPrefix(seccompProf, profileNamePrefix)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opts = append(opts,
|
opts = append(opts,
|
||||||
@ -768,6 +733,70 @@ func defaultRuntimeSpec() (*runtimespec.Spec, error) {
|
|||||||
return spec, nil
|
return spec, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generateSeccompSpecOpts generates containerd SpecOpts for seccomp.
|
||||||
|
func generateSeccompSpecOpts(seccompProf string, privileged, seccompEnabled bool) (containerd.SpecOpts, error) {
|
||||||
|
// Set seccomp profile
|
||||||
|
if seccompProf == runtimeDefault || seccompProf == dockerDefault {
|
||||||
|
// use correct default profile (Eg. if not configured otherwise, the default is docker/default)
|
||||||
|
seccompProf = seccompDefaultProfile
|
||||||
|
}
|
||||||
|
if !seccompEnabled {
|
||||||
|
if seccompProf != "" && seccompProf != unconfinedProfile {
|
||||||
|
return nil, fmt.Errorf("seccomp is not supported")
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if privileged {
|
||||||
|
// Do not set seccomp profile when container is privileged
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
switch seccompProf {
|
||||||
|
case "", unconfinedProfile:
|
||||||
|
// Do not set seccomp profile.
|
||||||
|
return nil, nil
|
||||||
|
case dockerDefault:
|
||||||
|
// Note: WithDefaultProfile specOpts must be added after capabilities
|
||||||
|
return seccomp.WithDefaultProfile(), nil
|
||||||
|
default:
|
||||||
|
// Require and Trim default profile name prefix
|
||||||
|
if !strings.HasPrefix(seccompProf, profileNamePrefix) {
|
||||||
|
return nil, fmt.Errorf("invalid seccomp profile %q", seccompProf)
|
||||||
|
}
|
||||||
|
return seccomp.WithProfile(strings.TrimPrefix(seccompProf, profileNamePrefix)), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateApparmorSpecOpts generates containerd SpecOpts for apparmor.
|
||||||
|
func generateApparmorSpecOpts(apparmorProf string, privileged, apparmorEnabled bool) (containerd.SpecOpts, error) {
|
||||||
|
if !apparmorEnabled {
|
||||||
|
// Should fail loudly if user try to specify apparmor profile
|
||||||
|
// but we don't support it.
|
||||||
|
if apparmorProf != "" {
|
||||||
|
return nil, fmt.Errorf("apparmor is not supported")
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
switch apparmorProf {
|
||||||
|
case runtimeDefault:
|
||||||
|
// TODO (mikebrow): delete created apparmor default profile
|
||||||
|
return apparmor.WithDefaultProfile(appArmorDefaultProfileName), nil
|
||||||
|
// TODO(random-liu): Should support "unconfined" after kubernetes#52395 lands.
|
||||||
|
case "":
|
||||||
|
// Based on kubernetes#51746, default apparmor profile should be applied
|
||||||
|
// for non-privileged container when apparmor is not specified.
|
||||||
|
if privileged {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return apparmor.WithDefaultProfile(appArmorDefaultProfileName), nil
|
||||||
|
default:
|
||||||
|
// Require and Trim default profile name prefix
|
||||||
|
if !strings.HasPrefix(apparmorProf, profileNamePrefix) {
|
||||||
|
return nil, fmt.Errorf("invalid apparmor profile %q", apparmorProf)
|
||||||
|
}
|
||||||
|
return apparmor.WithProfile(strings.TrimPrefix(apparmorProf, profileNamePrefix)), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure mount point on which path is mounted, is shared.
|
// Ensure mount point on which path is mounted, is shared.
|
||||||
func ensureShared(path string, mountInfos []*mount.Info) error {
|
func ensureShared(path string, mountInfos []*mount.Info) error {
|
||||||
sourceMount, optionalOpts, err := getSourceMount(path, mountInfos)
|
sourceMount, optionalOpts, err := getSourceMount(path, mountInfos)
|
||||||
|
@ -18,8 +18,12 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd"
|
||||||
|
"github.com/containerd/containerd/contrib/apparmor"
|
||||||
|
"github.com/containerd/containerd/contrib/seccomp"
|
||||||
"github.com/docker/docker/pkg/mount"
|
"github.com/docker/docker/pkg/mount"
|
||||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
@ -714,3 +718,115 @@ func TestDefaultRuntimeSpec(t *testing.T) {
|
|||||||
assert.NotEqual(t, "/run", mount.Destination)
|
assert.NotEqual(t, "/run", mount.Destination)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGenerateSeccompSpecOpts(t *testing.T) {
|
||||||
|
for desc, test := range map[string]struct {
|
||||||
|
profile string
|
||||||
|
privileged bool
|
||||||
|
disable bool
|
||||||
|
specOpts containerd.SpecOpts
|
||||||
|
expectErr bool
|
||||||
|
}{
|
||||||
|
"should return error if seccomp is specified when seccomp is not supported": {
|
||||||
|
profile: runtimeDefault,
|
||||||
|
disable: true,
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
"should not return error if seccomp is not specified when seccomp is not supported": {
|
||||||
|
profile: "",
|
||||||
|
disable: true,
|
||||||
|
},
|
||||||
|
"should not return error if seccomp is unconfined when seccomp is not supported": {
|
||||||
|
profile: unconfinedProfile,
|
||||||
|
disable: true,
|
||||||
|
},
|
||||||
|
"should not set seccomp when privileged is true": {
|
||||||
|
profile: seccompDefaultProfile,
|
||||||
|
privileged: true,
|
||||||
|
},
|
||||||
|
"should not set seccomp when seccomp is unconfined": {
|
||||||
|
profile: unconfinedProfile,
|
||||||
|
},
|
||||||
|
"should not set seccomp when seccomp is not specified": {
|
||||||
|
profile: "",
|
||||||
|
},
|
||||||
|
"should set default seccomp when seccomp is runtime/default": {
|
||||||
|
profile: runtimeDefault,
|
||||||
|
specOpts: seccomp.WithDefaultProfile(),
|
||||||
|
},
|
||||||
|
"should set default seccomp when seccomp is docker/default": {
|
||||||
|
profile: dockerDefault,
|
||||||
|
specOpts: seccomp.WithDefaultProfile(),
|
||||||
|
},
|
||||||
|
"should set specified profile when local profile is specified": {
|
||||||
|
profile: profileNamePrefix + "test-profile",
|
||||||
|
specOpts: seccomp.WithProfile("test-profile"),
|
||||||
|
},
|
||||||
|
"should return error if specified profile is invalid": {
|
||||||
|
profile: "test-profile",
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Logf("TestCase %q", desc)
|
||||||
|
specOpts, err := generateSeccompSpecOpts(test.profile, test.privileged, !test.disable)
|
||||||
|
assert.Equal(t,
|
||||||
|
reflect.ValueOf(test.specOpts).Pointer(),
|
||||||
|
reflect.ValueOf(specOpts).Pointer())
|
||||||
|
if test.expectErr {
|
||||||
|
assert.Error(t, err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateApparmorSpecOpts(t *testing.T) {
|
||||||
|
for desc, test := range map[string]struct {
|
||||||
|
profile string
|
||||||
|
privileged bool
|
||||||
|
disable bool
|
||||||
|
specOpts containerd.SpecOpts
|
||||||
|
expectErr bool
|
||||||
|
}{
|
||||||
|
"should return error if apparmor is specified when apparmor is not supported": {
|
||||||
|
profile: runtimeDefault,
|
||||||
|
disable: true,
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
"should not return error if apparmor is not specified when apparmor is not supported": {
|
||||||
|
profile: "",
|
||||||
|
disable: true,
|
||||||
|
},
|
||||||
|
"should set default apparmor when apparmor is not specified": {
|
||||||
|
profile: "",
|
||||||
|
specOpts: apparmor.WithDefaultProfile(appArmorDefaultProfileName),
|
||||||
|
},
|
||||||
|
"should not apparmor when apparmor is not specified and privileged is true": {
|
||||||
|
profile: "",
|
||||||
|
privileged: true,
|
||||||
|
},
|
||||||
|
"should set default apparmor when apparmor is runtime/default": {
|
||||||
|
profile: runtimeDefault,
|
||||||
|
specOpts: apparmor.WithDefaultProfile(appArmorDefaultProfileName),
|
||||||
|
},
|
||||||
|
"should set specified profile when local profile is specified": {
|
||||||
|
profile: profileNamePrefix + "test-profile",
|
||||||
|
specOpts: apparmor.WithProfile("test-profile"),
|
||||||
|
},
|
||||||
|
"should return error if specified profile is invalid": {
|
||||||
|
profile: "test-profile",
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Logf("TestCase %q", desc)
|
||||||
|
specOpts, err := generateApparmorSpecOpts(test.profile, test.privileged, !test.disable)
|
||||||
|
assert.Equal(t,
|
||||||
|
reflect.ValueOf(test.specOpts).Pointer(),
|
||||||
|
reflect.ValueOf(specOpts).Pointer())
|
||||||
|
if test.expectErr {
|
||||||
|
assert.Error(t, err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -22,11 +22,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
"github.com/containerd/containerd/contrib/seccomp"
|
|
||||||
"github.com/containerd/typeurl"
|
"github.com/containerd/typeurl"
|
||||||
"github.com/cri-o/ocicni/pkg/ocicni"
|
"github.com/cri-o/ocicni/pkg/ocicni"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
runcseccomp "github.com/opencontainers/runc/libcontainer/seccomp"
|
||||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/opencontainers/runtime-tools/generate"
|
"github.com/opencontainers/runtime-tools/generate"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
@ -76,8 +76,9 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get sandbox image %q: %v", c.config.SandboxImage, err)
|
return nil, fmt.Errorf("failed to get sandbox image %q: %v", c.config.SandboxImage, err)
|
||||||
}
|
}
|
||||||
|
securityContext := config.GetLinux().GetSecurityContext()
|
||||||
//Create Network Namespace if it is not in host network
|
//Create Network Namespace if it is not in host network
|
||||||
hostNet := config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostNetwork()
|
hostNet := securityContext.GetNamespaceOptions().GetHostNetwork()
|
||||||
if !hostNet {
|
if !hostNet {
|
||||||
// If it is not in host network namespace then create a namespace and set the sandbox
|
// If it is not in host network namespace then create a namespace and set the sandbox
|
||||||
// handle. NetNSPath in sandbox metadata and NetNS is non empty only for non host network
|
// handle. NetNSPath in sandbox metadata and NetNS is non empty only for non host network
|
||||||
@ -125,36 +126,19 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
|
|||||||
glog.V(4).Infof("Sandbox container spec: %+v", spec)
|
glog.V(4).Infof("Sandbox container spec: %+v", spec)
|
||||||
|
|
||||||
var specOpts []containerd.SpecOpts
|
var specOpts []containerd.SpecOpts
|
||||||
if uid := config.GetLinux().GetSecurityContext().GetRunAsUser(); uid != nil {
|
if uid := securityContext.GetRunAsUser(); uid != nil {
|
||||||
specOpts = append(specOpts, containerd.WithUserID(uint32(uid.GetValue())))
|
specOpts = append(specOpts, containerd.WithUserID(uint32(uid.GetValue())))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set seccomp profile
|
seccompSpecOpts, err := generateSeccompSpecOpts(
|
||||||
seccompProf := config.GetLinux().GetSecurityContext().GetSeccompProfilePath()
|
securityContext.GetSeccompProfilePath(),
|
||||||
if seccompProf == runtimeDefault || seccompProf == dockerDefault {
|
securityContext.GetPrivileged(),
|
||||||
// use correct default profile (Eg. if not configured otherwise, the default is docker/default for pods)
|
runcseccomp.IsEnabled())
|
||||||
seccompProf = seccompDefaultSandboxProfile
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to generate seccomp spec opts: %v", err)
|
||||||
}
|
}
|
||||||
|
if seccompSpecOpts != nil {
|
||||||
// TODO (mikebrow): consider a fuction for the logic used in sandbox and container for secccomp
|
specOpts = append(specOpts, seccompSpecOpts)
|
||||||
// Unset the seccomp profile, if seccomp is not enabled, unconfined, unset, or the security context is privileged
|
|
||||||
if !seccompEnabled ||
|
|
||||||
seccompProf == unconfinedProfile ||
|
|
||||||
seccompProf == "" ||
|
|
||||||
config.GetLinux().GetSecurityContext().GetPrivileged() {
|
|
||||||
spec.Linux.Seccomp = nil
|
|
||||||
} else {
|
|
||||||
switch seccompProf {
|
|
||||||
case dockerDefault:
|
|
||||||
// Note: WithDefaultProfile specOpts must be added after capabilities
|
|
||||||
specOpts = append(specOpts, seccomp.WithDefaultProfile())
|
|
||||||
default:
|
|
||||||
// Require and Trim default profile name prefix
|
|
||||||
if !strings.HasPrefix(seccompProf, profileNamePrefix) {
|
|
||||||
return nil, fmt.Errorf("invalid seccomp profile %q", seccompProf)
|
|
||||||
}
|
|
||||||
specOpts = append(specOpts, seccomp.WithProfile(strings.TrimPrefix(seccompProf, profileNamePrefix)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := []containerd.NewContainerOpts{
|
opts := []containerd.NewContainerOpts{
|
||||||
|
@ -46,6 +46,7 @@ github.com/pkg/errors v0.8.0
|
|||||||
github.com/pmezard/go-difflib v1.0.0
|
github.com/pmezard/go-difflib v1.0.0
|
||||||
github.com/PuerkitoBio/purell v1.0.0
|
github.com/PuerkitoBio/purell v1.0.0
|
||||||
github.com/PuerkitoBio/urlesc 5bd2802263f21d8788851d5305584c82a5c75d7e
|
github.com/PuerkitoBio/urlesc 5bd2802263f21d8788851d5305584c82a5c75d7e
|
||||||
|
github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
|
||||||
github.com/sirupsen/logrus v1.0.0
|
github.com/sirupsen/logrus v1.0.0
|
||||||
github.com/spf13/pflag 9ff6c6923cfffbcd502984b8e0c80539a94968b7
|
github.com/spf13/pflag 9ff6c6923cfffbcd502984b8e0c80539a94968b7
|
||||||
github.com/stretchr/testify v1.1.4
|
github.com/stretchr/testify v1.1.4
|
||||||
|
76
vendor/github.com/opencontainers/runc/libcontainer/seccomp/config.go
generated
vendored
Normal file
76
vendor/github.com/opencontainers/runc/libcontainer/seccomp/config.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package seccomp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
)
|
||||||
|
|
||||||
|
var operators = map[string]configs.Operator{
|
||||||
|
"SCMP_CMP_NE": configs.NotEqualTo,
|
||||||
|
"SCMP_CMP_LT": configs.LessThan,
|
||||||
|
"SCMP_CMP_LE": configs.LessThanOrEqualTo,
|
||||||
|
"SCMP_CMP_EQ": configs.EqualTo,
|
||||||
|
"SCMP_CMP_GE": configs.GreaterThanOrEqualTo,
|
||||||
|
"SCMP_CMP_GT": configs.GreaterThan,
|
||||||
|
"SCMP_CMP_MASKED_EQ": configs.MaskEqualTo,
|
||||||
|
}
|
||||||
|
|
||||||
|
var actions = map[string]configs.Action{
|
||||||
|
"SCMP_ACT_KILL": configs.Kill,
|
||||||
|
"SCMP_ACT_ERRNO": configs.Errno,
|
||||||
|
"SCMP_ACT_TRAP": configs.Trap,
|
||||||
|
"SCMP_ACT_ALLOW": configs.Allow,
|
||||||
|
"SCMP_ACT_TRACE": configs.Trace,
|
||||||
|
}
|
||||||
|
|
||||||
|
var archs = map[string]string{
|
||||||
|
"SCMP_ARCH_X86": "x86",
|
||||||
|
"SCMP_ARCH_X86_64": "amd64",
|
||||||
|
"SCMP_ARCH_X32": "x32",
|
||||||
|
"SCMP_ARCH_ARM": "arm",
|
||||||
|
"SCMP_ARCH_AARCH64": "arm64",
|
||||||
|
"SCMP_ARCH_MIPS": "mips",
|
||||||
|
"SCMP_ARCH_MIPS64": "mips64",
|
||||||
|
"SCMP_ARCH_MIPS64N32": "mips64n32",
|
||||||
|
"SCMP_ARCH_MIPSEL": "mipsel",
|
||||||
|
"SCMP_ARCH_MIPSEL64": "mipsel64",
|
||||||
|
"SCMP_ARCH_MIPSEL64N32": "mipsel64n32",
|
||||||
|
"SCMP_ARCH_PPC": "ppc",
|
||||||
|
"SCMP_ARCH_PPC64": "ppc64",
|
||||||
|
"SCMP_ARCH_PPC64LE": "ppc64le",
|
||||||
|
"SCMP_ARCH_S390": "s390",
|
||||||
|
"SCMP_ARCH_S390X": "s390x",
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertStringToOperator converts a string into a Seccomp comparison operator.
|
||||||
|
// Comparison operators use the names they are assigned by Libseccomp's header.
|
||||||
|
// Attempting to convert a string that is not a valid operator results in an
|
||||||
|
// error.
|
||||||
|
func ConvertStringToOperator(in string) (configs.Operator, error) {
|
||||||
|
if op, ok := operators[in]; ok == true {
|
||||||
|
return op, nil
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("string %s is not a valid operator for seccomp", in)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertStringToAction converts a string into a Seccomp rule match action.
|
||||||
|
// Actions use the names they are assigned in Libseccomp's header, though some
|
||||||
|
// (notable, SCMP_ACT_TRACE) are not available in this implementation and will
|
||||||
|
// return errors.
|
||||||
|
// Attempting to convert a string that is not a valid action results in an
|
||||||
|
// error.
|
||||||
|
func ConvertStringToAction(in string) (configs.Action, error) {
|
||||||
|
if act, ok := actions[in]; ok == true {
|
||||||
|
return act, nil
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("string %s is not a valid action for seccomp", in)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertStringToArch converts a string into a Seccomp comparison arch.
|
||||||
|
func ConvertStringToArch(in string) (string, error) {
|
||||||
|
if arch, ok := archs[in]; ok == true {
|
||||||
|
return arch, nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("string %s is not a valid arch for seccomp", in)
|
||||||
|
}
|
227
vendor/github.com/opencontainers/runc/libcontainer/seccomp/seccomp_linux.go
generated
vendored
Normal file
227
vendor/github.com/opencontainers/runc/libcontainer/seccomp/seccomp_linux.go
generated
vendored
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
// +build linux,cgo,seccomp
|
||||||
|
|
||||||
|
package seccomp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
libseccomp "github.com/seccomp/libseccomp-golang"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
actAllow = libseccomp.ActAllow
|
||||||
|
actTrap = libseccomp.ActTrap
|
||||||
|
actKill = libseccomp.ActKill
|
||||||
|
actTrace = libseccomp.ActTrace.SetReturnCode(int16(unix.EPERM))
|
||||||
|
actErrno = libseccomp.ActErrno.SetReturnCode(int16(unix.EPERM))
|
||||||
|
)
|
||||||
|
|
||||||
|
// Filters given syscalls in a container, preventing them from being used
|
||||||
|
// Started in the container init process, and carried over to all child processes
|
||||||
|
// Setns calls, however, require a separate invocation, as they are not children
|
||||||
|
// of the init until they join the namespace
|
||||||
|
func InitSeccomp(config *configs.Seccomp) error {
|
||||||
|
if config == nil {
|
||||||
|
return fmt.Errorf("cannot initialize Seccomp - nil config passed")
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultAction, err := getAction(config.DefaultAction)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error initializing seccomp - invalid default action")
|
||||||
|
}
|
||||||
|
|
||||||
|
filter, err := libseccomp.NewFilter(defaultAction)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error creating filter: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add extra architectures
|
||||||
|
for _, arch := range config.Architectures {
|
||||||
|
scmpArch, err := libseccomp.GetArchFromString(arch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := filter.AddArch(scmpArch); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset no new privs bit
|
||||||
|
if err := filter.SetNoNewPrivsBit(false); err != nil {
|
||||||
|
return fmt.Errorf("error setting no new privileges: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a rule for each syscall
|
||||||
|
for _, call := range config.Syscalls {
|
||||||
|
if call == nil {
|
||||||
|
return fmt.Errorf("encountered nil syscall while initializing Seccomp")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = matchCall(filter, call); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = filter.Load(); err != nil {
|
||||||
|
return fmt.Errorf("error loading seccomp filter into kernel: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEnabled returns if the kernel has been configured to support seccomp.
|
||||||
|
func IsEnabled() bool {
|
||||||
|
// Try to read from /proc/self/status for kernels > 3.8
|
||||||
|
s, err := parseStatusFile("/proc/self/status")
|
||||||
|
if err != nil {
|
||||||
|
// Check if Seccomp is supported, via CONFIG_SECCOMP.
|
||||||
|
if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
|
||||||
|
// Make sure the kernel has CONFIG_SECCOMP_FILTER.
|
||||||
|
if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, ok := s["Seccomp"]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert Libcontainer Action to Libseccomp ScmpAction
|
||||||
|
func getAction(act configs.Action) (libseccomp.ScmpAction, error) {
|
||||||
|
switch act {
|
||||||
|
case configs.Kill:
|
||||||
|
return actKill, nil
|
||||||
|
case configs.Errno:
|
||||||
|
return actErrno, nil
|
||||||
|
case configs.Trap:
|
||||||
|
return actTrap, nil
|
||||||
|
case configs.Allow:
|
||||||
|
return actAllow, nil
|
||||||
|
case configs.Trace:
|
||||||
|
return actTrace, nil
|
||||||
|
default:
|
||||||
|
return libseccomp.ActInvalid, fmt.Errorf("invalid action, cannot use in rule")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert Libcontainer Operator to Libseccomp ScmpCompareOp
|
||||||
|
func getOperator(op configs.Operator) (libseccomp.ScmpCompareOp, error) {
|
||||||
|
switch op {
|
||||||
|
case configs.EqualTo:
|
||||||
|
return libseccomp.CompareEqual, nil
|
||||||
|
case configs.NotEqualTo:
|
||||||
|
return libseccomp.CompareNotEqual, nil
|
||||||
|
case configs.GreaterThan:
|
||||||
|
return libseccomp.CompareGreater, nil
|
||||||
|
case configs.GreaterThanOrEqualTo:
|
||||||
|
return libseccomp.CompareGreaterEqual, nil
|
||||||
|
case configs.LessThan:
|
||||||
|
return libseccomp.CompareLess, nil
|
||||||
|
case configs.LessThanOrEqualTo:
|
||||||
|
return libseccomp.CompareLessOrEqual, nil
|
||||||
|
case configs.MaskEqualTo:
|
||||||
|
return libseccomp.CompareMaskedEqual, nil
|
||||||
|
default:
|
||||||
|
return libseccomp.CompareInvalid, fmt.Errorf("invalid operator, cannot use in rule")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert Libcontainer Arg to Libseccomp ScmpCondition
|
||||||
|
func getCondition(arg *configs.Arg) (libseccomp.ScmpCondition, error) {
|
||||||
|
cond := libseccomp.ScmpCondition{}
|
||||||
|
|
||||||
|
if arg == nil {
|
||||||
|
return cond, fmt.Errorf("cannot convert nil to syscall condition")
|
||||||
|
}
|
||||||
|
|
||||||
|
op, err := getOperator(arg.Op)
|
||||||
|
if err != nil {
|
||||||
|
return cond, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return libseccomp.MakeCondition(arg.Index, op, arg.Value, arg.ValueTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a rule to match a single syscall
|
||||||
|
func matchCall(filter *libseccomp.ScmpFilter, call *configs.Syscall) error {
|
||||||
|
if call == nil || filter == nil {
|
||||||
|
return fmt.Errorf("cannot use nil as syscall to block")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(call.Name) == 0 {
|
||||||
|
return fmt.Errorf("empty string is not a valid syscall")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we can't resolve the syscall, assume it's not supported on this kernel
|
||||||
|
// Ignore it, don't error out
|
||||||
|
callNum, err := libseccomp.GetSyscallFromName(call.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the call's action to the libseccomp equivalent
|
||||||
|
callAct, err := getAction(call.Action)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unconditional match - just add the rule
|
||||||
|
if len(call.Args) == 0 {
|
||||||
|
if err = filter.AddRule(callNum, callAct); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Conditional match - convert the per-arg rules into library format
|
||||||
|
conditions := []libseccomp.ScmpCondition{}
|
||||||
|
|
||||||
|
for _, cond := range call.Args {
|
||||||
|
newCond, err := getCondition(cond)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
conditions = append(conditions, newCond)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = filter.AddRuleConditional(callNum, callAct, conditions); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseStatusFile(path string) (map[string]string, error) {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
s := bufio.NewScanner(f)
|
||||||
|
status := make(map[string]string)
|
||||||
|
|
||||||
|
for s.Scan() {
|
||||||
|
text := s.Text()
|
||||||
|
parts := strings.Split(text, ":")
|
||||||
|
|
||||||
|
if len(parts) <= 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
status[parts[0]] = parts[1]
|
||||||
|
}
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return status, nil
|
||||||
|
}
|
24
vendor/github.com/opencontainers/runc/libcontainer/seccomp/seccomp_unsupported.go
generated
vendored
Normal file
24
vendor/github.com/opencontainers/runc/libcontainer/seccomp/seccomp_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// +build !linux !cgo !seccomp
|
||||||
|
|
||||||
|
package seccomp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrSeccompNotEnabled = errors.New("seccomp: config provided but seccomp not supported")
|
||||||
|
|
||||||
|
// InitSeccomp does nothing because seccomp is not supported.
|
||||||
|
func InitSeccomp(config *configs.Seccomp) error {
|
||||||
|
if config != nil {
|
||||||
|
return ErrSeccompNotEnabled
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEnabled returns false, because it is not supported.
|
||||||
|
func IsEnabled() bool {
|
||||||
|
return false
|
||||||
|
}
|
22
vendor/github.com/seccomp/libseccomp-golang/LICENSE
generated
vendored
Normal file
22
vendor/github.com/seccomp/libseccomp-golang/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Copyright (c) 2015 Matthew Heon <mheon@redhat.com>
|
||||||
|
Copyright (c) 2015 Paul Moore <pmoore@redhat.com>
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
- Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
- Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
vendor/github.com/seccomp/libseccomp-golang/README
generated
vendored
Normal file
26
vendor/github.com/seccomp/libseccomp-golang/README
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
libseccomp-golang: Go Language Bindings for the libseccomp Project
|
||||||
|
===============================================================================
|
||||||
|
https://github.com/seccomp/libseccomp-golang
|
||||||
|
https://github.com/seccomp/libseccomp
|
||||||
|
|
||||||
|
The libseccomp library provides an easy to use, platform independent, interface
|
||||||
|
to the Linux Kernel's syscall filtering mechanism. The libseccomp API is
|
||||||
|
designed to abstract away the underlying BPF based syscall filter language and
|
||||||
|
present a more conventional function-call based filtering interface that should
|
||||||
|
be familiar to, and easily adopted by, application developers.
|
||||||
|
|
||||||
|
The libseccomp-golang library provides a Go based interface to the libseccomp
|
||||||
|
library.
|
||||||
|
|
||||||
|
* Online Resources
|
||||||
|
|
||||||
|
The library source repository currently lives on GitHub at the following URLs:
|
||||||
|
|
||||||
|
-> https://github.com/seccomp/libseccomp-golang
|
||||||
|
-> https://github.com/seccomp/libseccomp
|
||||||
|
|
||||||
|
The project mailing list is currently hosted on Google Groups at the URL below,
|
||||||
|
please note that a Google account is not required to subscribe to the mailing
|
||||||
|
list.
|
||||||
|
|
||||||
|
-> https://groups.google.com/d/forum/libseccomp
|
857
vendor/github.com/seccomp/libseccomp-golang/seccomp.go
generated
vendored
Normal file
857
vendor/github.com/seccomp/libseccomp-golang/seccomp.go
generated
vendored
Normal file
@ -0,0 +1,857 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
// Public API specification for libseccomp Go bindings
|
||||||
|
// Contains public API for the bindings
|
||||||
|
|
||||||
|
// Package seccomp provides bindings for libseccomp, a library wrapping the Linux
|
||||||
|
// seccomp syscall. Seccomp enables an application to restrict system call use
|
||||||
|
// for itself and its children.
|
||||||
|
package seccomp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// C wrapping code
|
||||||
|
|
||||||
|
// #cgo pkg-config: libseccomp
|
||||||
|
// #include <stdlib.h>
|
||||||
|
// #include <seccomp.h>
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// Exported types
|
||||||
|
|
||||||
|
// ScmpArch represents a CPU architecture. Seccomp can restrict syscalls on a
|
||||||
|
// per-architecture basis.
|
||||||
|
type ScmpArch uint
|
||||||
|
|
||||||
|
// ScmpAction represents an action to be taken on a filter rule match in
|
||||||
|
// libseccomp
|
||||||
|
type ScmpAction uint
|
||||||
|
|
||||||
|
// ScmpCompareOp represents a comparison operator which can be used in a filter
|
||||||
|
// rule
|
||||||
|
type ScmpCompareOp uint
|
||||||
|
|
||||||
|
// ScmpCondition represents a rule in a libseccomp filter context
|
||||||
|
type ScmpCondition struct {
|
||||||
|
Argument uint `json:"argument,omitempty"`
|
||||||
|
Op ScmpCompareOp `json:"operator,omitempty"`
|
||||||
|
Operand1 uint64 `json:"operand_one,omitempty"`
|
||||||
|
Operand2 uint64 `json:"operand_two,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScmpSyscall represents a Linux System Call
|
||||||
|
type ScmpSyscall int32
|
||||||
|
|
||||||
|
// Exported Constants
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Valid architectures recognized by libseccomp
|
||||||
|
// ARM64 and all MIPS architectures are unsupported by versions of the
|
||||||
|
// library before v2.2 and will return errors if used
|
||||||
|
|
||||||
|
// ArchInvalid is a placeholder to ensure uninitialized ScmpArch
|
||||||
|
// variables are invalid
|
||||||
|
ArchInvalid ScmpArch = iota
|
||||||
|
// ArchNative is the native architecture of the kernel
|
||||||
|
ArchNative ScmpArch = iota
|
||||||
|
// ArchX86 represents 32-bit x86 syscalls
|
||||||
|
ArchX86 ScmpArch = iota
|
||||||
|
// ArchAMD64 represents 64-bit x86-64 syscalls
|
||||||
|
ArchAMD64 ScmpArch = iota
|
||||||
|
// ArchX32 represents 64-bit x86-64 syscalls (32-bit pointers)
|
||||||
|
ArchX32 ScmpArch = iota
|
||||||
|
// ArchARM represents 32-bit ARM syscalls
|
||||||
|
ArchARM ScmpArch = iota
|
||||||
|
// ArchARM64 represents 64-bit ARM syscalls
|
||||||
|
ArchARM64 ScmpArch = iota
|
||||||
|
// ArchMIPS represents 32-bit MIPS syscalls
|
||||||
|
ArchMIPS ScmpArch = iota
|
||||||
|
// ArchMIPS64 represents 64-bit MIPS syscalls
|
||||||
|
ArchMIPS64 ScmpArch = iota
|
||||||
|
// ArchMIPS64N32 represents 64-bit MIPS syscalls (32-bit pointers)
|
||||||
|
ArchMIPS64N32 ScmpArch = iota
|
||||||
|
// ArchMIPSEL represents 32-bit MIPS syscalls (little endian)
|
||||||
|
ArchMIPSEL ScmpArch = iota
|
||||||
|
// ArchMIPSEL64 represents 64-bit MIPS syscalls (little endian)
|
||||||
|
ArchMIPSEL64 ScmpArch = iota
|
||||||
|
// ArchMIPSEL64N32 represents 64-bit MIPS syscalls (little endian,
|
||||||
|
// 32-bit pointers)
|
||||||
|
ArchMIPSEL64N32 ScmpArch = iota
|
||||||
|
// ArchPPC represents 32-bit POWERPC syscalls
|
||||||
|
ArchPPC ScmpArch = iota
|
||||||
|
// ArchPPC64 represents 64-bit POWER syscalls (big endian)
|
||||||
|
ArchPPC64 ScmpArch = iota
|
||||||
|
// ArchPPC64LE represents 64-bit POWER syscalls (little endian)
|
||||||
|
ArchPPC64LE ScmpArch = iota
|
||||||
|
// ArchS390 represents 31-bit System z/390 syscalls
|
||||||
|
ArchS390 ScmpArch = iota
|
||||||
|
// ArchS390X represents 64-bit System z/390 syscalls
|
||||||
|
ArchS390X ScmpArch = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Supported actions on filter match
|
||||||
|
|
||||||
|
// ActInvalid is a placeholder to ensure uninitialized ScmpAction
|
||||||
|
// variables are invalid
|
||||||
|
ActInvalid ScmpAction = iota
|
||||||
|
// ActKill kills the process
|
||||||
|
ActKill ScmpAction = iota
|
||||||
|
// ActTrap throws SIGSYS
|
||||||
|
ActTrap ScmpAction = iota
|
||||||
|
// ActErrno causes the syscall to return a negative error code. This
|
||||||
|
// code can be set with the SetReturnCode method
|
||||||
|
ActErrno ScmpAction = iota
|
||||||
|
// ActTrace causes the syscall to notify tracing processes with the
|
||||||
|
// given error code. This code can be set with the SetReturnCode method
|
||||||
|
ActTrace ScmpAction = iota
|
||||||
|
// ActAllow permits the syscall to continue execution
|
||||||
|
ActAllow ScmpAction = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// These are comparison operators used in conditional seccomp rules
|
||||||
|
// They are used to compare the value of a single argument of a syscall
|
||||||
|
// against a user-defined constant
|
||||||
|
|
||||||
|
// CompareInvalid is a placeholder to ensure uninitialized ScmpCompareOp
|
||||||
|
// variables are invalid
|
||||||
|
CompareInvalid ScmpCompareOp = iota
|
||||||
|
// CompareNotEqual returns true if the argument is not equal to the
|
||||||
|
// given value
|
||||||
|
CompareNotEqual ScmpCompareOp = iota
|
||||||
|
// CompareLess returns true if the argument is less than the given value
|
||||||
|
CompareLess ScmpCompareOp = iota
|
||||||
|
// CompareLessOrEqual returns true if the argument is less than or equal
|
||||||
|
// to the given value
|
||||||
|
CompareLessOrEqual ScmpCompareOp = iota
|
||||||
|
// CompareEqual returns true if the argument is equal to the given value
|
||||||
|
CompareEqual ScmpCompareOp = iota
|
||||||
|
// CompareGreaterEqual returns true if the argument is greater than or
|
||||||
|
// equal to the given value
|
||||||
|
CompareGreaterEqual ScmpCompareOp = iota
|
||||||
|
// CompareGreater returns true if the argument is greater than the given
|
||||||
|
// value
|
||||||
|
CompareGreater ScmpCompareOp = iota
|
||||||
|
// CompareMaskedEqual returns true if the argument is equal to the given
|
||||||
|
// value, when masked (bitwise &) against the second given value
|
||||||
|
CompareMaskedEqual ScmpCompareOp = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
// Helpers for types
|
||||||
|
|
||||||
|
// GetArchFromString returns an ScmpArch constant from a string representing an
|
||||||
|
// architecture
|
||||||
|
func GetArchFromString(arch string) (ScmpArch, error) {
|
||||||
|
switch strings.ToLower(arch) {
|
||||||
|
case "x86":
|
||||||
|
return ArchX86, nil
|
||||||
|
case "amd64", "x86-64", "x86_64", "x64":
|
||||||
|
return ArchAMD64, nil
|
||||||
|
case "x32":
|
||||||
|
return ArchX32, nil
|
||||||
|
case "arm":
|
||||||
|
return ArchARM, nil
|
||||||
|
case "arm64", "aarch64":
|
||||||
|
return ArchARM64, nil
|
||||||
|
case "mips":
|
||||||
|
return ArchMIPS, nil
|
||||||
|
case "mips64":
|
||||||
|
return ArchMIPS64, nil
|
||||||
|
case "mips64n32":
|
||||||
|
return ArchMIPS64N32, nil
|
||||||
|
case "mipsel":
|
||||||
|
return ArchMIPSEL, nil
|
||||||
|
case "mipsel64":
|
||||||
|
return ArchMIPSEL64, nil
|
||||||
|
case "mipsel64n32":
|
||||||
|
return ArchMIPSEL64N32, nil
|
||||||
|
case "ppc":
|
||||||
|
return ArchPPC, nil
|
||||||
|
case "ppc64":
|
||||||
|
return ArchPPC64, nil
|
||||||
|
case "ppc64le":
|
||||||
|
return ArchPPC64LE, nil
|
||||||
|
case "s390":
|
||||||
|
return ArchS390, nil
|
||||||
|
case "s390x":
|
||||||
|
return ArchS390X, nil
|
||||||
|
default:
|
||||||
|
return ArchInvalid, fmt.Errorf("cannot convert unrecognized string %s", arch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of an architecture constant
|
||||||
|
func (a ScmpArch) String() string {
|
||||||
|
switch a {
|
||||||
|
case ArchX86:
|
||||||
|
return "x86"
|
||||||
|
case ArchAMD64:
|
||||||
|
return "amd64"
|
||||||
|
case ArchX32:
|
||||||
|
return "x32"
|
||||||
|
case ArchARM:
|
||||||
|
return "arm"
|
||||||
|
case ArchARM64:
|
||||||
|
return "arm64"
|
||||||
|
case ArchMIPS:
|
||||||
|
return "mips"
|
||||||
|
case ArchMIPS64:
|
||||||
|
return "mips64"
|
||||||
|
case ArchMIPS64N32:
|
||||||
|
return "mips64n32"
|
||||||
|
case ArchMIPSEL:
|
||||||
|
return "mipsel"
|
||||||
|
case ArchMIPSEL64:
|
||||||
|
return "mipsel64"
|
||||||
|
case ArchMIPSEL64N32:
|
||||||
|
return "mipsel64n32"
|
||||||
|
case ArchPPC:
|
||||||
|
return "ppc"
|
||||||
|
case ArchPPC64:
|
||||||
|
return "ppc64"
|
||||||
|
case ArchPPC64LE:
|
||||||
|
return "ppc64le"
|
||||||
|
case ArchS390:
|
||||||
|
return "s390"
|
||||||
|
case ArchS390X:
|
||||||
|
return "s390x"
|
||||||
|
case ArchNative:
|
||||||
|
return "native"
|
||||||
|
case ArchInvalid:
|
||||||
|
return "Invalid architecture"
|
||||||
|
default:
|
||||||
|
return "Unknown architecture"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of a comparison operator constant
|
||||||
|
func (a ScmpCompareOp) String() string {
|
||||||
|
switch a {
|
||||||
|
case CompareNotEqual:
|
||||||
|
return "Not equal"
|
||||||
|
case CompareLess:
|
||||||
|
return "Less than"
|
||||||
|
case CompareLessOrEqual:
|
||||||
|
return "Less than or equal to"
|
||||||
|
case CompareEqual:
|
||||||
|
return "Equal"
|
||||||
|
case CompareGreaterEqual:
|
||||||
|
return "Greater than or equal to"
|
||||||
|
case CompareGreater:
|
||||||
|
return "Greater than"
|
||||||
|
case CompareMaskedEqual:
|
||||||
|
return "Masked equality"
|
||||||
|
case CompareInvalid:
|
||||||
|
return "Invalid comparison operator"
|
||||||
|
default:
|
||||||
|
return "Unrecognized comparison operator"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of a seccomp match action
|
||||||
|
func (a ScmpAction) String() string {
|
||||||
|
switch a & 0xFFFF {
|
||||||
|
case ActKill:
|
||||||
|
return "Action: Kill Process"
|
||||||
|
case ActTrap:
|
||||||
|
return "Action: Send SIGSYS"
|
||||||
|
case ActErrno:
|
||||||
|
return fmt.Sprintf("Action: Return error code %d", (a >> 16))
|
||||||
|
case ActTrace:
|
||||||
|
return fmt.Sprintf("Action: Notify tracing processes with code %d",
|
||||||
|
(a >> 16))
|
||||||
|
case ActAllow:
|
||||||
|
return "Action: Allow system call"
|
||||||
|
default:
|
||||||
|
return "Unrecognized Action"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReturnCode adds a return code to a supporting ScmpAction, clearing any
|
||||||
|
// existing code Only valid on ActErrno and ActTrace. Takes no action otherwise.
|
||||||
|
// Accepts 16-bit return code as argument.
|
||||||
|
// Returns a valid ScmpAction of the original type with the new error code set.
|
||||||
|
func (a ScmpAction) SetReturnCode(code int16) ScmpAction {
|
||||||
|
aTmp := a & 0x0000FFFF
|
||||||
|
if aTmp == ActErrno || aTmp == ActTrace {
|
||||||
|
return (aTmp | (ScmpAction(code)&0xFFFF)<<16)
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReturnCode returns the return code of an ScmpAction
|
||||||
|
func (a ScmpAction) GetReturnCode() int16 {
|
||||||
|
return int16(a >> 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
// General utility functions
|
||||||
|
|
||||||
|
// GetLibraryVersion returns the version of the library the bindings are built
|
||||||
|
// against.
|
||||||
|
// The version is formatted as follows: Major.Minor.Micro
|
||||||
|
func GetLibraryVersion() (major, minor, micro int) {
|
||||||
|
return verMajor, verMinor, verMicro
|
||||||
|
}
|
||||||
|
|
||||||
|
// Syscall functions
|
||||||
|
|
||||||
|
// GetName retrieves the name of a syscall from its number.
|
||||||
|
// Acts on any syscall number.
|
||||||
|
// Returns either a string containing the name of the syscall, or an error.
|
||||||
|
func (s ScmpSyscall) GetName() (string, error) {
|
||||||
|
return s.GetNameByArch(ArchNative)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNameByArch retrieves the name of a syscall from its number for a given
|
||||||
|
// architecture.
|
||||||
|
// Acts on any syscall number.
|
||||||
|
// Accepts a valid architecture constant.
|
||||||
|
// Returns either a string containing the name of the syscall, or an error.
|
||||||
|
// if the syscall is unrecognized or an issue occurred.
|
||||||
|
func (s ScmpSyscall) GetNameByArch(arch ScmpArch) (string, error) {
|
||||||
|
if err := sanitizeArch(arch); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
cString := C.seccomp_syscall_resolve_num_arch(arch.toNative(), C.int(s))
|
||||||
|
if cString == nil {
|
||||||
|
return "", fmt.Errorf("could not resolve syscall name")
|
||||||
|
}
|
||||||
|
defer C.free(unsafe.Pointer(cString))
|
||||||
|
|
||||||
|
finalStr := C.GoString(cString)
|
||||||
|
return finalStr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSyscallFromName returns the number of a syscall by name on the kernel's
|
||||||
|
// native architecture.
|
||||||
|
// Accepts a string containing the name of a syscall.
|
||||||
|
// Returns the number of the syscall, or an error if no syscall with that name
|
||||||
|
// was found.
|
||||||
|
func GetSyscallFromName(name string) (ScmpSyscall, error) {
|
||||||
|
cString := C.CString(name)
|
||||||
|
defer C.free(unsafe.Pointer(cString))
|
||||||
|
|
||||||
|
result := C.seccomp_syscall_resolve_name(cString)
|
||||||
|
if result == scmpError {
|
||||||
|
return 0, fmt.Errorf("could not resolve name to syscall")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ScmpSyscall(result), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSyscallFromNameByArch returns the number of a syscall by name for a given
|
||||||
|
// architecture's ABI.
|
||||||
|
// Accepts the name of a syscall and an architecture constant.
|
||||||
|
// Returns the number of the syscall, or an error if an invalid architecture is
|
||||||
|
// passed or a syscall with that name was not found.
|
||||||
|
func GetSyscallFromNameByArch(name string, arch ScmpArch) (ScmpSyscall, error) {
|
||||||
|
if err := sanitizeArch(arch); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cString := C.CString(name)
|
||||||
|
defer C.free(unsafe.Pointer(cString))
|
||||||
|
|
||||||
|
result := C.seccomp_syscall_resolve_name_arch(arch.toNative(), cString)
|
||||||
|
if result == scmpError {
|
||||||
|
return 0, fmt.Errorf("could not resolve name to syscall")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ScmpSyscall(result), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeCondition creates and returns a new condition to attach to a filter rule.
|
||||||
|
// Associated rules will only match if this condition is true.
|
||||||
|
// Accepts the number the argument we are checking, and a comparison operator
|
||||||
|
// and value to compare to.
|
||||||
|
// The rule will match if argument $arg (zero-indexed) of the syscall is
|
||||||
|
// $COMPARE_OP the provided comparison value.
|
||||||
|
// Some comparison operators accept two values. Masked equals, for example,
|
||||||
|
// will mask $arg of the syscall with the second value provided (via bitwise
|
||||||
|
// AND) and then compare against the first value provided.
|
||||||
|
// For example, in the less than or equal case, if the syscall argument was
|
||||||
|
// 0 and the value provided was 1, the condition would match, as 0 is less
|
||||||
|
// than or equal to 1.
|
||||||
|
// Return either an error on bad argument or a valid ScmpCondition struct.
|
||||||
|
func MakeCondition(arg uint, comparison ScmpCompareOp, values ...uint64) (ScmpCondition, error) {
|
||||||
|
var condStruct ScmpCondition
|
||||||
|
|
||||||
|
if comparison == CompareInvalid {
|
||||||
|
return condStruct, fmt.Errorf("invalid comparison operator")
|
||||||
|
} else if arg > 5 {
|
||||||
|
return condStruct, fmt.Errorf("syscalls only have up to 6 arguments")
|
||||||
|
} else if len(values) > 2 {
|
||||||
|
return condStruct, fmt.Errorf("conditions can have at most 2 arguments")
|
||||||
|
} else if len(values) == 0 {
|
||||||
|
return condStruct, fmt.Errorf("must provide at least one value to compare against")
|
||||||
|
}
|
||||||
|
|
||||||
|
condStruct.Argument = arg
|
||||||
|
condStruct.Op = comparison
|
||||||
|
condStruct.Operand1 = values[0]
|
||||||
|
if len(values) == 2 {
|
||||||
|
condStruct.Operand2 = values[1]
|
||||||
|
} else {
|
||||||
|
condStruct.Operand2 = 0 // Unused
|
||||||
|
}
|
||||||
|
|
||||||
|
return condStruct, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility Functions
|
||||||
|
|
||||||
|
// GetNativeArch returns architecture token representing the native kernel
|
||||||
|
// architecture
|
||||||
|
func GetNativeArch() (ScmpArch, error) {
|
||||||
|
arch := C.seccomp_arch_native()
|
||||||
|
|
||||||
|
return archFromNative(arch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public Filter API
|
||||||
|
|
||||||
|
// ScmpFilter represents a filter context in libseccomp.
|
||||||
|
// A filter context is initially empty. Rules can be added to it, and it can
|
||||||
|
// then be loaded into the kernel.
|
||||||
|
type ScmpFilter struct {
|
||||||
|
filterCtx C.scmp_filter_ctx
|
||||||
|
valid bool
|
||||||
|
lock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFilter creates and returns a new filter context.
|
||||||
|
// Accepts a default action to be taken for syscalls which match no rules in
|
||||||
|
// the filter.
|
||||||
|
// Returns a reference to a valid filter context, or nil and an error if the
|
||||||
|
// filter context could not be created or an invalid default action was given.
|
||||||
|
func NewFilter(defaultAction ScmpAction) (*ScmpFilter, error) {
|
||||||
|
if err := sanitizeAction(defaultAction); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fPtr := C.seccomp_init(defaultAction.toNative())
|
||||||
|
if fPtr == nil {
|
||||||
|
return nil, fmt.Errorf("could not create filter")
|
||||||
|
}
|
||||||
|
|
||||||
|
filter := new(ScmpFilter)
|
||||||
|
filter.filterCtx = fPtr
|
||||||
|
filter.valid = true
|
||||||
|
runtime.SetFinalizer(filter, filterFinalizer)
|
||||||
|
|
||||||
|
return filter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid determines whether a filter context is valid to use.
|
||||||
|
// Some operations (Release and Merge) render filter contexts invalid and
|
||||||
|
// consequently prevent further use.
|
||||||
|
func (f *ScmpFilter) IsValid() bool {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
return f.valid
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset resets a filter context, removing all its existing state.
|
||||||
|
// Accepts a new default action to be taken for syscalls which do not match.
|
||||||
|
// Returns an error if the filter or action provided are invalid.
|
||||||
|
func (f *ScmpFilter) Reset(defaultAction ScmpAction) error {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
if err := sanitizeAction(defaultAction); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !f.valid {
|
||||||
|
return errBadFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
retCode := C.seccomp_reset(f.filterCtx, defaultAction.toNative())
|
||||||
|
if retCode != 0 {
|
||||||
|
return syscall.Errno(-1 * retCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release releases a filter context, freeing its memory. Should be called after
|
||||||
|
// loading into the kernel, when the filter is no longer needed.
|
||||||
|
// After calling this function, the given filter is no longer valid and cannot
|
||||||
|
// be used.
|
||||||
|
// Release() will be invoked automatically when a filter context is garbage
|
||||||
|
// collected, but can also be called manually to free memory.
|
||||||
|
func (f *ScmpFilter) Release() {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
if !f.valid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.valid = false
|
||||||
|
C.seccomp_release(f.filterCtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge merges two filter contexts.
|
||||||
|
// The source filter src will be released as part of the process, and will no
|
||||||
|
// longer be usable or valid after this call.
|
||||||
|
// To be merged, filters must NOT share any architectures, and all their
|
||||||
|
// attributes (Default Action, Bad Arch Action, No New Privs and TSync bools)
|
||||||
|
// must match.
|
||||||
|
// The filter src will be merged into the filter this is called on.
|
||||||
|
// The architectures of the src filter not present in the destination, and all
|
||||||
|
// associated rules, will be added to the destination.
|
||||||
|
// Returns an error if merging the filters failed.
|
||||||
|
func (f *ScmpFilter) Merge(src *ScmpFilter) error {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
src.lock.Lock()
|
||||||
|
defer src.lock.Unlock()
|
||||||
|
|
||||||
|
if !src.valid || !f.valid {
|
||||||
|
return fmt.Errorf("one or more of the filter contexts is invalid or uninitialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge the filters
|
||||||
|
retCode := C.seccomp_merge(f.filterCtx, src.filterCtx)
|
||||||
|
if syscall.Errno(-1*retCode) == syscall.EINVAL {
|
||||||
|
return fmt.Errorf("filters could not be merged due to a mismatch in attributes or invalid filter")
|
||||||
|
} else if retCode != 0 {
|
||||||
|
return syscall.Errno(-1 * retCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
src.valid = false
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsArchPresent checks if an architecture is present in a filter.
|
||||||
|
// If a filter contains an architecture, it uses its default action for
|
||||||
|
// syscalls which do not match rules in it, and its rules can match syscalls
|
||||||
|
// for that ABI.
|
||||||
|
// If a filter does not contain an architecture, all syscalls made to that
|
||||||
|
// kernel ABI will fail with the filter's default Bad Architecture Action
|
||||||
|
// (by default, killing the process).
|
||||||
|
// Accepts an architecture constant.
|
||||||
|
// Returns true if the architecture is present in the filter, false otherwise,
|
||||||
|
// and an error on an invalid filter context, architecture constant, or an
|
||||||
|
// issue with the call to libseccomp.
|
||||||
|
func (f *ScmpFilter) IsArchPresent(arch ScmpArch) (bool, error) {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
if err := sanitizeArch(arch); err != nil {
|
||||||
|
return false, err
|
||||||
|
} else if !f.valid {
|
||||||
|
return false, errBadFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
retCode := C.seccomp_arch_exist(f.filterCtx, arch.toNative())
|
||||||
|
if syscall.Errno(-1*retCode) == syscall.EEXIST {
|
||||||
|
// -EEXIST is "arch not present"
|
||||||
|
return false, nil
|
||||||
|
} else if retCode != 0 {
|
||||||
|
return false, syscall.Errno(-1 * retCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddArch adds an architecture to the filter.
|
||||||
|
// Accepts an architecture constant.
|
||||||
|
// Returns an error on invalid filter context or architecture token, or an
|
||||||
|
// issue with the call to libseccomp.
|
||||||
|
func (f *ScmpFilter) AddArch(arch ScmpArch) error {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
if err := sanitizeArch(arch); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !f.valid {
|
||||||
|
return errBadFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Libseccomp returns -EEXIST if the specified architecture is already
|
||||||
|
// present. Succeed silently in this case, as it's not fatal, and the
|
||||||
|
// architecture is present already.
|
||||||
|
retCode := C.seccomp_arch_add(f.filterCtx, arch.toNative())
|
||||||
|
if retCode != 0 && syscall.Errno(-1*retCode) != syscall.EEXIST {
|
||||||
|
return syscall.Errno(-1 * retCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveArch removes an architecture from the filter.
|
||||||
|
// Accepts an architecture constant.
|
||||||
|
// Returns an error on invalid filter context or architecture token, or an
|
||||||
|
// issue with the call to libseccomp.
|
||||||
|
func (f *ScmpFilter) RemoveArch(arch ScmpArch) error {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
if err := sanitizeArch(arch); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !f.valid {
|
||||||
|
return errBadFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Similar to AddArch, -EEXIST is returned if the arch is not present
|
||||||
|
// Succeed silently in that case, this is not fatal and the architecture
|
||||||
|
// is not present in the filter after RemoveArch
|
||||||
|
retCode := C.seccomp_arch_remove(f.filterCtx, arch.toNative())
|
||||||
|
if retCode != 0 && syscall.Errno(-1*retCode) != syscall.EEXIST {
|
||||||
|
return syscall.Errno(-1 * retCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load loads a filter context into the kernel.
|
||||||
|
// Returns an error if the filter context is invalid or the syscall failed.
|
||||||
|
func (f *ScmpFilter) Load() error {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
if !f.valid {
|
||||||
|
return errBadFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
if retCode := C.seccomp_load(f.filterCtx); retCode != 0 {
|
||||||
|
return syscall.Errno(-1 * retCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefaultAction returns the default action taken on a syscall which does not
|
||||||
|
// match a rule in the filter, or an error if an issue was encountered
|
||||||
|
// retrieving the value.
|
||||||
|
func (f *ScmpFilter) GetDefaultAction() (ScmpAction, error) {
|
||||||
|
action, err := f.getFilterAttr(filterAttrActDefault)
|
||||||
|
if err != nil {
|
||||||
|
return 0x0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return actionFromNative(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBadArchAction returns the default action taken on a syscall for an
|
||||||
|
// architecture not in the filter, or an error if an issue was encountered
|
||||||
|
// retrieving the value.
|
||||||
|
func (f *ScmpFilter) GetBadArchAction() (ScmpAction, error) {
|
||||||
|
action, err := f.getFilterAttr(filterAttrActBadArch)
|
||||||
|
if err != nil {
|
||||||
|
return 0x0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return actionFromNative(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNoNewPrivsBit returns the current state the No New Privileges bit will be set
|
||||||
|
// to on the filter being loaded, or an error if an issue was encountered
|
||||||
|
// retrieving the value.
|
||||||
|
// The No New Privileges bit tells the kernel that new processes run with exec()
|
||||||
|
// cannot gain more privileges than the process that ran exec().
|
||||||
|
// For example, a process with No New Privileges set would be unable to exec
|
||||||
|
// setuid/setgid executables.
|
||||||
|
func (f *ScmpFilter) GetNoNewPrivsBit() (bool, error) {
|
||||||
|
noNewPrivs, err := f.getFilterAttr(filterAttrNNP)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if noNewPrivs == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTsyncBit returns whether Thread Synchronization will be enabled on the
|
||||||
|
// filter being loaded, or an error if an issue was encountered retrieving the
|
||||||
|
// value.
|
||||||
|
// Thread Sync ensures that all members of the thread group of the calling
|
||||||
|
// process will share the same Seccomp filter set.
|
||||||
|
// Tsync is a fairly recent addition to the Linux kernel and older kernels
|
||||||
|
// lack support. If the running kernel does not support Tsync and it is
|
||||||
|
// requested in a filter, Libseccomp will not enable TSync support and will
|
||||||
|
// proceed as normal.
|
||||||
|
// This function is unavailable before v2.2 of libseccomp and will return an
|
||||||
|
// error.
|
||||||
|
func (f *ScmpFilter) GetTsyncBit() (bool, error) {
|
||||||
|
tSync, err := f.getFilterAttr(filterAttrTsync)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if tSync == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBadArchAction sets the default action taken on a syscall for an
|
||||||
|
// architecture not in the filter, or an error if an issue was encountered
|
||||||
|
// setting the value.
|
||||||
|
func (f *ScmpFilter) SetBadArchAction(action ScmpAction) error {
|
||||||
|
if err := sanitizeAction(action); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.setFilterAttr(filterAttrActBadArch, action.toNative())
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNoNewPrivsBit sets the state of the No New Privileges bit, which will be
|
||||||
|
// applied on filter load, or an error if an issue was encountered setting the
|
||||||
|
// value.
|
||||||
|
// Filters with No New Privileges set to 0 can only be loaded if the process
|
||||||
|
// has the CAP_SYS_ADMIN capability.
|
||||||
|
func (f *ScmpFilter) SetNoNewPrivsBit(state bool) error {
|
||||||
|
var toSet C.uint32_t = 0x0
|
||||||
|
|
||||||
|
if state {
|
||||||
|
toSet = 0x1
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.setFilterAttr(filterAttrNNP, toSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTsync sets whether Thread Synchronization will be enabled on the filter
|
||||||
|
// being loaded. Returns an error if setting Tsync failed, or the filter is
|
||||||
|
// invalid.
|
||||||
|
// Thread Sync ensures that all members of the thread group of the calling
|
||||||
|
// process will share the same Seccomp filter set.
|
||||||
|
// Tsync is a fairly recent addition to the Linux kernel and older kernels
|
||||||
|
// lack support. If the running kernel does not support Tsync and it is
|
||||||
|
// requested in a filter, Libseccomp will not enable TSync support and will
|
||||||
|
// proceed as normal.
|
||||||
|
// This function is unavailable before v2.2 of libseccomp and will return an
|
||||||
|
// error.
|
||||||
|
func (f *ScmpFilter) SetTsync(enable bool) error {
|
||||||
|
var toSet C.uint32_t = 0x0
|
||||||
|
|
||||||
|
if enable {
|
||||||
|
toSet = 0x1
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.setFilterAttr(filterAttrTsync, toSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSyscallPriority sets a syscall's priority.
|
||||||
|
// This provides a hint to the filter generator in libseccomp about the
|
||||||
|
// importance of this syscall. High-priority syscalls are placed
|
||||||
|
// first in the filter code, and incur less overhead (at the expense of
|
||||||
|
// lower-priority syscalls).
|
||||||
|
func (f *ScmpFilter) SetSyscallPriority(call ScmpSyscall, priority uint8) error {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
if !f.valid {
|
||||||
|
return errBadFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
if retCode := C.seccomp_syscall_priority(f.filterCtx, C.int(call),
|
||||||
|
C.uint8_t(priority)); retCode != 0 {
|
||||||
|
return syscall.Errno(-1 * retCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRule adds a single rule for an unconditional action on a syscall.
|
||||||
|
// Accepts the number of the syscall and the action to be taken on the call
|
||||||
|
// being made.
|
||||||
|
// Returns an error if an issue was encountered adding the rule.
|
||||||
|
func (f *ScmpFilter) AddRule(call ScmpSyscall, action ScmpAction) error {
|
||||||
|
return f.addRuleGeneric(call, action, false, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRuleExact adds a single rule for an unconditional action on a syscall.
|
||||||
|
// Accepts the number of the syscall and the action to be taken on the call
|
||||||
|
// being made.
|
||||||
|
// No modifications will be made to the rule, and it will fail to add if it
|
||||||
|
// cannot be applied to the current architecture without modification.
|
||||||
|
// The rule will function exactly as described, but it may not function identically
|
||||||
|
// (or be able to be applied to) all architectures.
|
||||||
|
// Returns an error if an issue was encountered adding the rule.
|
||||||
|
func (f *ScmpFilter) AddRuleExact(call ScmpSyscall, action ScmpAction) error {
|
||||||
|
return f.addRuleGeneric(call, action, true, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRuleConditional adds a single rule for a conditional action on a syscall.
|
||||||
|
// Returns an error if an issue was encountered adding the rule.
|
||||||
|
// All conditions must match for the rule to match.
|
||||||
|
// There is a bug in library versions below v2.2.1 which can, in some cases,
|
||||||
|
// cause conditions to be lost when more than one are used. Consequently,
|
||||||
|
// AddRuleConditional is disabled on library versions lower than v2.2.1
|
||||||
|
func (f *ScmpFilter) AddRuleConditional(call ScmpSyscall, action ScmpAction, conds []ScmpCondition) error {
|
||||||
|
return f.addRuleGeneric(call, action, false, conds)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRuleConditionalExact adds a single rule for a conditional action on a
|
||||||
|
// syscall.
|
||||||
|
// No modifications will be made to the rule, and it will fail to add if it
|
||||||
|
// cannot be applied to the current architecture without modification.
|
||||||
|
// The rule will function exactly as described, but it may not function identically
|
||||||
|
// (or be able to be applied to) all architectures.
|
||||||
|
// Returns an error if an issue was encountered adding the rule.
|
||||||
|
// There is a bug in library versions below v2.2.1 which can, in some cases,
|
||||||
|
// cause conditions to be lost when more than one are used. Consequently,
|
||||||
|
// AddRuleConditionalExact is disabled on library versions lower than v2.2.1
|
||||||
|
func (f *ScmpFilter) AddRuleConditionalExact(call ScmpSyscall, action ScmpAction, conds []ScmpCondition) error {
|
||||||
|
return f.addRuleGeneric(call, action, true, conds)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportPFC output PFC-formatted, human-readable dump of a filter context's
|
||||||
|
// rules to a file.
|
||||||
|
// Accepts file to write to (must be open for writing).
|
||||||
|
// Returns an error if writing to the file fails.
|
||||||
|
func (f *ScmpFilter) ExportPFC(file *os.File) error {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
fd := file.Fd()
|
||||||
|
|
||||||
|
if !f.valid {
|
||||||
|
return errBadFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
if retCode := C.seccomp_export_pfc(f.filterCtx, C.int(fd)); retCode != 0 {
|
||||||
|
return syscall.Errno(-1 * retCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportBPF outputs Berkeley Packet Filter-formatted, kernel-readable dump of a
|
||||||
|
// filter context's rules to a file.
|
||||||
|
// Accepts file to write to (must be open for writing).
|
||||||
|
// Returns an error if writing to the file fails.
|
||||||
|
func (f *ScmpFilter) ExportBPF(file *os.File) error {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
fd := file.Fd()
|
||||||
|
|
||||||
|
if !f.valid {
|
||||||
|
return errBadFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
if retCode := C.seccomp_export_bpf(f.filterCtx, C.int(fd)); retCode != 0 {
|
||||||
|
return syscall.Errno(-1 * retCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
506
vendor/github.com/seccomp/libseccomp-golang/seccomp_internal.go
generated
vendored
Normal file
506
vendor/github.com/seccomp/libseccomp-golang/seccomp_internal.go
generated
vendored
Normal file
@ -0,0 +1,506 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
// Internal functions for libseccomp Go bindings
|
||||||
|
// No exported functions
|
||||||
|
|
||||||
|
package seccomp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Unexported C wrapping code - provides the C-Golang interface
|
||||||
|
// Get the seccomp header in scope
|
||||||
|
// Need stdlib.h for free() on cstrings
|
||||||
|
|
||||||
|
// #cgo pkg-config: libseccomp
|
||||||
|
/*
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <seccomp.h>
|
||||||
|
|
||||||
|
#if SCMP_VER_MAJOR < 2
|
||||||
|
#error Minimum supported version of Libseccomp is v2.1.0
|
||||||
|
#elif SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 1
|
||||||
|
#error Minimum supported version of Libseccomp is v2.1.0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ARCH_BAD ~0
|
||||||
|
|
||||||
|
const uint32_t C_ARCH_BAD = ARCH_BAD;
|
||||||
|
|
||||||
|
#ifndef SCMP_ARCH_AARCH64
|
||||||
|
#define SCMP_ARCH_AARCH64 ARCH_BAD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SCMP_ARCH_MIPS
|
||||||
|
#define SCMP_ARCH_MIPS ARCH_BAD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SCMP_ARCH_MIPS64
|
||||||
|
#define SCMP_ARCH_MIPS64 ARCH_BAD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SCMP_ARCH_MIPS64N32
|
||||||
|
#define SCMP_ARCH_MIPS64N32 ARCH_BAD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SCMP_ARCH_MIPSEL
|
||||||
|
#define SCMP_ARCH_MIPSEL ARCH_BAD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SCMP_ARCH_MIPSEL64
|
||||||
|
#define SCMP_ARCH_MIPSEL64 ARCH_BAD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SCMP_ARCH_MIPSEL64N32
|
||||||
|
#define SCMP_ARCH_MIPSEL64N32 ARCH_BAD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SCMP_ARCH_PPC
|
||||||
|
#define SCMP_ARCH_PPC ARCH_BAD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SCMP_ARCH_PPC64
|
||||||
|
#define SCMP_ARCH_PPC64 ARCH_BAD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SCMP_ARCH_PPC64LE
|
||||||
|
#define SCMP_ARCH_PPC64LE ARCH_BAD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SCMP_ARCH_S390
|
||||||
|
#define SCMP_ARCH_S390 ARCH_BAD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SCMP_ARCH_S390X
|
||||||
|
#define SCMP_ARCH_S390X ARCH_BAD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const uint32_t C_ARCH_NATIVE = SCMP_ARCH_NATIVE;
|
||||||
|
const uint32_t C_ARCH_X86 = SCMP_ARCH_X86;
|
||||||
|
const uint32_t C_ARCH_X86_64 = SCMP_ARCH_X86_64;
|
||||||
|
const uint32_t C_ARCH_X32 = SCMP_ARCH_X32;
|
||||||
|
const uint32_t C_ARCH_ARM = SCMP_ARCH_ARM;
|
||||||
|
const uint32_t C_ARCH_AARCH64 = SCMP_ARCH_AARCH64;
|
||||||
|
const uint32_t C_ARCH_MIPS = SCMP_ARCH_MIPS;
|
||||||
|
const uint32_t C_ARCH_MIPS64 = SCMP_ARCH_MIPS64;
|
||||||
|
const uint32_t C_ARCH_MIPS64N32 = SCMP_ARCH_MIPS64N32;
|
||||||
|
const uint32_t C_ARCH_MIPSEL = SCMP_ARCH_MIPSEL;
|
||||||
|
const uint32_t C_ARCH_MIPSEL64 = SCMP_ARCH_MIPSEL64;
|
||||||
|
const uint32_t C_ARCH_MIPSEL64N32 = SCMP_ARCH_MIPSEL64N32;
|
||||||
|
const uint32_t C_ARCH_PPC = SCMP_ARCH_PPC;
|
||||||
|
const uint32_t C_ARCH_PPC64 = SCMP_ARCH_PPC64;
|
||||||
|
const uint32_t C_ARCH_PPC64LE = SCMP_ARCH_PPC64LE;
|
||||||
|
const uint32_t C_ARCH_S390 = SCMP_ARCH_S390;
|
||||||
|
const uint32_t C_ARCH_S390X = SCMP_ARCH_S390X;
|
||||||
|
|
||||||
|
const uint32_t C_ACT_KILL = SCMP_ACT_KILL;
|
||||||
|
const uint32_t C_ACT_TRAP = SCMP_ACT_TRAP;
|
||||||
|
const uint32_t C_ACT_ERRNO = SCMP_ACT_ERRNO(0);
|
||||||
|
const uint32_t C_ACT_TRACE = SCMP_ACT_TRACE(0);
|
||||||
|
const uint32_t C_ACT_ALLOW = SCMP_ACT_ALLOW;
|
||||||
|
|
||||||
|
// If TSync is not supported, make sure it doesn't map to a supported filter attribute
|
||||||
|
// Don't worry about major version < 2, the minimum version checks should catch that case
|
||||||
|
#if SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 2
|
||||||
|
#define SCMP_FLTATR_CTL_TSYNC _SCMP_CMP_MIN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const uint32_t C_ATTRIBUTE_DEFAULT = (uint32_t)SCMP_FLTATR_ACT_DEFAULT;
|
||||||
|
const uint32_t C_ATTRIBUTE_BADARCH = (uint32_t)SCMP_FLTATR_ACT_BADARCH;
|
||||||
|
const uint32_t C_ATTRIBUTE_NNP = (uint32_t)SCMP_FLTATR_CTL_NNP;
|
||||||
|
const uint32_t C_ATTRIBUTE_TSYNC = (uint32_t)SCMP_FLTATR_CTL_TSYNC;
|
||||||
|
|
||||||
|
const int C_CMP_NE = (int)SCMP_CMP_NE;
|
||||||
|
const int C_CMP_LT = (int)SCMP_CMP_LT;
|
||||||
|
const int C_CMP_LE = (int)SCMP_CMP_LE;
|
||||||
|
const int C_CMP_EQ = (int)SCMP_CMP_EQ;
|
||||||
|
const int C_CMP_GE = (int)SCMP_CMP_GE;
|
||||||
|
const int C_CMP_GT = (int)SCMP_CMP_GT;
|
||||||
|
const int C_CMP_MASKED_EQ = (int)SCMP_CMP_MASKED_EQ;
|
||||||
|
|
||||||
|
const int C_VERSION_MAJOR = SCMP_VER_MAJOR;
|
||||||
|
const int C_VERSION_MINOR = SCMP_VER_MINOR;
|
||||||
|
const int C_VERSION_MICRO = SCMP_VER_MICRO;
|
||||||
|
|
||||||
|
typedef struct scmp_arg_cmp* scmp_cast_t;
|
||||||
|
|
||||||
|
// Wrapper to create an scmp_arg_cmp struct
|
||||||
|
void*
|
||||||
|
make_struct_arg_cmp(
|
||||||
|
unsigned int arg,
|
||||||
|
int compare,
|
||||||
|
uint64_t a,
|
||||||
|
uint64_t b
|
||||||
|
)
|
||||||
|
{
|
||||||
|
struct scmp_arg_cmp *s = malloc(sizeof(struct scmp_arg_cmp));
|
||||||
|
|
||||||
|
s->arg = arg;
|
||||||
|
s->op = compare;
|
||||||
|
s->datum_a = a;
|
||||||
|
s->datum_b = b;
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// Nonexported types
|
||||||
|
type scmpFilterAttr uint32
|
||||||
|
|
||||||
|
// Nonexported constants
|
||||||
|
|
||||||
|
const (
|
||||||
|
filterAttrActDefault scmpFilterAttr = iota
|
||||||
|
filterAttrActBadArch scmpFilterAttr = iota
|
||||||
|
filterAttrNNP scmpFilterAttr = iota
|
||||||
|
filterAttrTsync scmpFilterAttr = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// An error return from certain libseccomp functions
|
||||||
|
scmpError C.int = -1
|
||||||
|
// Comparison boundaries to check for architecture validity
|
||||||
|
archStart ScmpArch = ArchNative
|
||||||
|
archEnd ScmpArch = ArchS390X
|
||||||
|
// Comparison boundaries to check for action validity
|
||||||
|
actionStart ScmpAction = ActKill
|
||||||
|
actionEnd ScmpAction = ActAllow
|
||||||
|
// Comparison boundaries to check for comparison operator validity
|
||||||
|
compareOpStart ScmpCompareOp = CompareNotEqual
|
||||||
|
compareOpEnd ScmpCompareOp = CompareMaskedEqual
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Error thrown on bad filter context
|
||||||
|
errBadFilter = fmt.Errorf("filter is invalid or uninitialized")
|
||||||
|
// Constants representing library major, minor, and micro versions
|
||||||
|
verMajor = int(C.C_VERSION_MAJOR)
|
||||||
|
verMinor = int(C.C_VERSION_MINOR)
|
||||||
|
verMicro = int(C.C_VERSION_MICRO)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Nonexported functions
|
||||||
|
|
||||||
|
// Check if library version is greater than or equal to the given one
|
||||||
|
func checkVersionAbove(major, minor, micro int) bool {
|
||||||
|
return (verMajor > major) ||
|
||||||
|
(verMajor == major && verMinor > minor) ||
|
||||||
|
(verMajor == major && verMinor == minor && verMicro >= micro)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init function: Verify library version is appropriate
|
||||||
|
func init() {
|
||||||
|
if !checkVersionAbove(2, 1, 0) {
|
||||||
|
fmt.Fprintf(os.Stderr, "Libseccomp version too low: minimum supported is 2.1.0, detected %d.%d.%d", C.C_VERSION_MAJOR, C.C_VERSION_MINOR, C.C_VERSION_MICRO)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter helpers
|
||||||
|
|
||||||
|
// Filter finalizer - ensure that kernel context for filters is freed
|
||||||
|
func filterFinalizer(f *ScmpFilter) {
|
||||||
|
f.Release()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a raw filter attribute
|
||||||
|
func (f *ScmpFilter) getFilterAttr(attr scmpFilterAttr) (C.uint32_t, error) {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
if !f.valid {
|
||||||
|
return 0x0, errBadFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
if !checkVersionAbove(2, 2, 0) && attr == filterAttrTsync {
|
||||||
|
return 0x0, fmt.Errorf("the thread synchronization attribute is not supported in this version of the library")
|
||||||
|
}
|
||||||
|
|
||||||
|
var attribute C.uint32_t
|
||||||
|
|
||||||
|
retCode := C.seccomp_attr_get(f.filterCtx, attr.toNative(), &attribute)
|
||||||
|
if retCode != 0 {
|
||||||
|
return 0x0, syscall.Errno(-1 * retCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return attribute, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a raw filter attribute
|
||||||
|
func (f *ScmpFilter) setFilterAttr(attr scmpFilterAttr, value C.uint32_t) error {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
if !f.valid {
|
||||||
|
return errBadFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
if !checkVersionAbove(2, 2, 0) && attr == filterAttrTsync {
|
||||||
|
return fmt.Errorf("the thread synchronization attribute is not supported in this version of the library")
|
||||||
|
}
|
||||||
|
|
||||||
|
retCode := C.seccomp_attr_set(f.filterCtx, attr.toNative(), value)
|
||||||
|
if retCode != 0 {
|
||||||
|
return syscall.Errno(-1 * retCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DOES NOT LOCK OR CHECK VALIDITY
|
||||||
|
// Assumes caller has already done this
|
||||||
|
// Wrapper for seccomp_rule_add_... functions
|
||||||
|
func (f *ScmpFilter) addRuleWrapper(call ScmpSyscall, action ScmpAction, exact bool, cond C.scmp_cast_t) error {
|
||||||
|
var length C.uint
|
||||||
|
if cond != nil {
|
||||||
|
length = 1
|
||||||
|
} else {
|
||||||
|
length = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var retCode C.int
|
||||||
|
if exact {
|
||||||
|
retCode = C.seccomp_rule_add_exact_array(f.filterCtx, action.toNative(), C.int(call), length, cond)
|
||||||
|
} else {
|
||||||
|
retCode = C.seccomp_rule_add_array(f.filterCtx, action.toNative(), C.int(call), length, cond)
|
||||||
|
}
|
||||||
|
|
||||||
|
if syscall.Errno(-1*retCode) == syscall.EFAULT {
|
||||||
|
return fmt.Errorf("unrecognized syscall")
|
||||||
|
} else if syscall.Errno(-1*retCode) == syscall.EPERM {
|
||||||
|
return fmt.Errorf("requested action matches default action of filter")
|
||||||
|
} else if retCode != 0 {
|
||||||
|
return syscall.Errno(-1 * retCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic add function for filter rules
|
||||||
|
func (f *ScmpFilter) addRuleGeneric(call ScmpSyscall, action ScmpAction, exact bool, conds []ScmpCondition) error {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
if !f.valid {
|
||||||
|
return errBadFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(conds) == 0 {
|
||||||
|
if err := f.addRuleWrapper(call, action, exact, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We don't support conditional filtering in library version v2.1
|
||||||
|
if !checkVersionAbove(2, 2, 1) {
|
||||||
|
return fmt.Errorf("conditional filtering requires libseccomp version >= 2.2.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cond := range conds {
|
||||||
|
cmpStruct := C.make_struct_arg_cmp(C.uint(cond.Argument), cond.Op.toNative(), C.uint64_t(cond.Operand1), C.uint64_t(cond.Operand2))
|
||||||
|
defer C.free(cmpStruct)
|
||||||
|
|
||||||
|
if err := f.addRuleWrapper(call, action, exact, C.scmp_cast_t(cmpStruct)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic Helpers
|
||||||
|
|
||||||
|
// Helper - Sanitize Arch token input
|
||||||
|
func sanitizeArch(in ScmpArch) error {
|
||||||
|
if in < archStart || in > archEnd {
|
||||||
|
return fmt.Errorf("unrecognized architecture")
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.toNative() == C.C_ARCH_BAD {
|
||||||
|
return fmt.Errorf("architecture is not supported on this version of the library")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanitizeAction(in ScmpAction) error {
|
||||||
|
inTmp := in & 0x0000FFFF
|
||||||
|
if inTmp < actionStart || inTmp > actionEnd {
|
||||||
|
return fmt.Errorf("unrecognized action")
|
||||||
|
}
|
||||||
|
|
||||||
|
if inTmp != ActTrace && inTmp != ActErrno && (in&0xFFFF0000) != 0 {
|
||||||
|
return fmt.Errorf("highest 16 bits must be zeroed except for Trace and Errno")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanitizeCompareOp(in ScmpCompareOp) error {
|
||||||
|
if in < compareOpStart || in > compareOpEnd {
|
||||||
|
return fmt.Errorf("unrecognized comparison operator")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func archFromNative(a C.uint32_t) (ScmpArch, error) {
|
||||||
|
switch a {
|
||||||
|
case C.C_ARCH_X86:
|
||||||
|
return ArchX86, nil
|
||||||
|
case C.C_ARCH_X86_64:
|
||||||
|
return ArchAMD64, nil
|
||||||
|
case C.C_ARCH_X32:
|
||||||
|
return ArchX32, nil
|
||||||
|
case C.C_ARCH_ARM:
|
||||||
|
return ArchARM, nil
|
||||||
|
case C.C_ARCH_NATIVE:
|
||||||
|
return ArchNative, nil
|
||||||
|
case C.C_ARCH_AARCH64:
|
||||||
|
return ArchARM64, nil
|
||||||
|
case C.C_ARCH_MIPS:
|
||||||
|
return ArchMIPS, nil
|
||||||
|
case C.C_ARCH_MIPS64:
|
||||||
|
return ArchMIPS64, nil
|
||||||
|
case C.C_ARCH_MIPS64N32:
|
||||||
|
return ArchMIPS64N32, nil
|
||||||
|
case C.C_ARCH_MIPSEL:
|
||||||
|
return ArchMIPSEL, nil
|
||||||
|
case C.C_ARCH_MIPSEL64:
|
||||||
|
return ArchMIPSEL64, nil
|
||||||
|
case C.C_ARCH_MIPSEL64N32:
|
||||||
|
return ArchMIPSEL64N32, nil
|
||||||
|
case C.C_ARCH_PPC:
|
||||||
|
return ArchPPC, nil
|
||||||
|
case C.C_ARCH_PPC64:
|
||||||
|
return ArchPPC64, nil
|
||||||
|
case C.C_ARCH_PPC64LE:
|
||||||
|
return ArchPPC64LE, nil
|
||||||
|
case C.C_ARCH_S390:
|
||||||
|
return ArchS390, nil
|
||||||
|
case C.C_ARCH_S390X:
|
||||||
|
return ArchS390X, nil
|
||||||
|
default:
|
||||||
|
return 0x0, fmt.Errorf("unrecognized architecture")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only use with sanitized arches, no error handling
|
||||||
|
func (a ScmpArch) toNative() C.uint32_t {
|
||||||
|
switch a {
|
||||||
|
case ArchX86:
|
||||||
|
return C.C_ARCH_X86
|
||||||
|
case ArchAMD64:
|
||||||
|
return C.C_ARCH_X86_64
|
||||||
|
case ArchX32:
|
||||||
|
return C.C_ARCH_X32
|
||||||
|
case ArchARM:
|
||||||
|
return C.C_ARCH_ARM
|
||||||
|
case ArchARM64:
|
||||||
|
return C.C_ARCH_AARCH64
|
||||||
|
case ArchMIPS:
|
||||||
|
return C.C_ARCH_MIPS
|
||||||
|
case ArchMIPS64:
|
||||||
|
return C.C_ARCH_MIPS64
|
||||||
|
case ArchMIPS64N32:
|
||||||
|
return C.C_ARCH_MIPS64N32
|
||||||
|
case ArchMIPSEL:
|
||||||
|
return C.C_ARCH_MIPSEL
|
||||||
|
case ArchMIPSEL64:
|
||||||
|
return C.C_ARCH_MIPSEL64
|
||||||
|
case ArchMIPSEL64N32:
|
||||||
|
return C.C_ARCH_MIPSEL64N32
|
||||||
|
case ArchPPC:
|
||||||
|
return C.C_ARCH_PPC
|
||||||
|
case ArchPPC64:
|
||||||
|
return C.C_ARCH_PPC64
|
||||||
|
case ArchPPC64LE:
|
||||||
|
return C.C_ARCH_PPC64LE
|
||||||
|
case ArchS390:
|
||||||
|
return C.C_ARCH_S390
|
||||||
|
case ArchS390X:
|
||||||
|
return C.C_ARCH_S390X
|
||||||
|
case ArchNative:
|
||||||
|
return C.C_ARCH_NATIVE
|
||||||
|
default:
|
||||||
|
return 0x0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only use with sanitized ops, no error handling
|
||||||
|
func (a ScmpCompareOp) toNative() C.int {
|
||||||
|
switch a {
|
||||||
|
case CompareNotEqual:
|
||||||
|
return C.C_CMP_NE
|
||||||
|
case CompareLess:
|
||||||
|
return C.C_CMP_LT
|
||||||
|
case CompareLessOrEqual:
|
||||||
|
return C.C_CMP_LE
|
||||||
|
case CompareEqual:
|
||||||
|
return C.C_CMP_EQ
|
||||||
|
case CompareGreaterEqual:
|
||||||
|
return C.C_CMP_GE
|
||||||
|
case CompareGreater:
|
||||||
|
return C.C_CMP_GT
|
||||||
|
case CompareMaskedEqual:
|
||||||
|
return C.C_CMP_MASKED_EQ
|
||||||
|
default:
|
||||||
|
return 0x0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func actionFromNative(a C.uint32_t) (ScmpAction, error) {
|
||||||
|
aTmp := a & 0xFFFF
|
||||||
|
switch a & 0xFFFF0000 {
|
||||||
|
case C.C_ACT_KILL:
|
||||||
|
return ActKill, nil
|
||||||
|
case C.C_ACT_TRAP:
|
||||||
|
return ActTrap, nil
|
||||||
|
case C.C_ACT_ERRNO:
|
||||||
|
return ActErrno.SetReturnCode(int16(aTmp)), nil
|
||||||
|
case C.C_ACT_TRACE:
|
||||||
|
return ActTrace.SetReturnCode(int16(aTmp)), nil
|
||||||
|
case C.C_ACT_ALLOW:
|
||||||
|
return ActAllow, nil
|
||||||
|
default:
|
||||||
|
return 0x0, fmt.Errorf("unrecognized action")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only use with sanitized actions, no error handling
|
||||||
|
func (a ScmpAction) toNative() C.uint32_t {
|
||||||
|
switch a & 0xFFFF {
|
||||||
|
case ActKill:
|
||||||
|
return C.C_ACT_KILL
|
||||||
|
case ActTrap:
|
||||||
|
return C.C_ACT_TRAP
|
||||||
|
case ActErrno:
|
||||||
|
return C.C_ACT_ERRNO | (C.uint32_t(a) >> 16)
|
||||||
|
case ActTrace:
|
||||||
|
return C.C_ACT_TRACE | (C.uint32_t(a) >> 16)
|
||||||
|
case ActAllow:
|
||||||
|
return C.C_ACT_ALLOW
|
||||||
|
default:
|
||||||
|
return 0x0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal only, assumes safe attribute
|
||||||
|
func (a scmpFilterAttr) toNative() uint32 {
|
||||||
|
switch a {
|
||||||
|
case filterAttrActDefault:
|
||||||
|
return uint32(C.C_ATTRIBUTE_DEFAULT)
|
||||||
|
case filterAttrActBadArch:
|
||||||
|
return uint32(C.C_ATTRIBUTE_BADARCH)
|
||||||
|
case filterAttrNNP:
|
||||||
|
return uint32(C.C_ATTRIBUTE_NNP)
|
||||||
|
case filterAttrTsync:
|
||||||
|
return uint32(C.C_ATTRIBUTE_TSYNC)
|
||||||
|
default:
|
||||||
|
return 0x0
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user