Skip to content
This repository has been archived by the owner on Dec 13, 2018. It is now read-only.

Move Seccomp support to use Libseccomp as backend #633

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
FROM golang:1.4

RUN echo "deb http://ftp.us.debian.org/debian testing main contrib" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y iptables criu=1.5.2-1 && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y iptables criu=1.5.2-1 git build-essential autoconf libtool && rm -rf /var/lib/apt/lists/*

RUN go get golang.org/x/tools/cmd/cover

ENV GOPATH $GOPATH:/go/src/github.com/docker/libcontainer/vendor
RUN go get github.com/docker/docker/pkg/term

# Need Libseccomp v2.2.1
RUN git clone https://github.com/seccomp/libseccomp /libseccomp
RUN cd /libseccomp && git checkout v2.2.1 && ./autogen.sh && ./configure && make && make check && make install
# Fix linking error
RUN cp /usr/local/lib/libseccomp.so /usr/lib/libseccomp.so.2

# setup a playground for us to spawn containers in
RUN mkdir /busybox && \
curl -sSL 'https://github.com/jpetazzo/docker-busybox/raw/buildroot-2014.11/rootfs.tar' | tar -xC /busybox
Expand All @@ -19,7 +25,7 @@ COPY . /go/src/github.com/docker/libcontainer
WORKDIR /go/src/github.com/docker/libcontainer
RUN cp sample_configs/minimal.json /busybox/container.json

RUN make direct-install
RUN make TEST_TAGS='-tags seccomp' direct-install

ENTRYPOINT ["/dind"]
CMD ["make", "direct-test"]
CMD ["make", "TEST_TAGS=-tags seccomp", "direct-test"]
44 changes: 6 additions & 38 deletions configs/config.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package configs

import (
"github.com/docker/libcontainer/security/seccomp"
)

type Rlimit struct {
Type int `json:"type"`
Hard uint64 `json:"hard"`
Expand All @@ -13,40 +17,6 @@ type IDMap struct {
Size int `json:"size"`
}

type Seccomp struct {
Syscalls []*Syscall `json:"syscalls"`
}

type Action int

const (
Kill Action = iota - 3
Trap
Allow
)

type Operator int

const (
EqualTo Operator = iota
NotEqualTo
GreatherThan
LessThan
MaskEqualTo
)

type Arg struct {
Index int `json:"index"`
Value uint32 `json:"value"`
Op Operator `json:"op"`
}

type Syscall struct {
Value int `json:"value"`
Action Action `json:"action"`
Args []*Arg `json:"args"`
}

// TODO Windows. Many of these fields should be factored out into those parts
// which are common across platforms, and those which are platform specific.

Expand Down Expand Up @@ -139,8 +109,6 @@ type Config struct {
// sysctl -w my.property.name value in Linux.
SystemProperties map[string]string `json:"system_properties"`

// Seccomp allows actions to be taken whenever a syscall is made within the container.
// By default, all syscalls are allowed with actions to allow, trap, kill, or return an errno
// can be specified on a per syscall basis.
Seccomp *Seccomp `json:"seccomp"`
// SeccompConfig holds information on system calls to be restricted in the container
SeccompConfig *seccomp.Config `json:"seccomp_config,omitempty"`
}
59 changes: 0 additions & 59 deletions init_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/docker/libcontainer/cgroups"
"github.com/docker/libcontainer/configs"
"github.com/docker/libcontainer/netlink"
"github.com/docker/libcontainer/seccomp"
"github.com/docker/libcontainer/system"
"github.com/docker/libcontainer/user"
"github.com/docker/libcontainer/utils"
Expand Down Expand Up @@ -270,61 +269,3 @@ func killCgroupProcesses(m cgroups.Manager) error {
}
return nil
}

func finalizeSeccomp(config *initConfig) error {
if config.Config.Seccomp == nil {
return nil
}
context := seccomp.New()
for _, s := range config.Config.Seccomp.Syscalls {
ss := &seccomp.Syscall{
Value: uint32(s.Value),
Action: seccompAction(s.Action),
}
if len(s.Args) > 0 {
ss.Args = seccompArgs(s.Args)
}
context.Add(ss)
}
return context.Load()
}

func seccompAction(a configs.Action) seccomp.Action {
switch a {
case configs.Kill:
return seccomp.Kill
case configs.Trap:
return seccomp.Trap
case configs.Allow:
return seccomp.Allow
}
return seccomp.Error(syscall.Errno(int(a)))
}

func seccompArgs(args []*configs.Arg) seccomp.Args {
var sa []seccomp.Arg
for _, a := range args {
sa = append(sa, seccomp.Arg{
Index: uint32(a.Index),
Op: seccompOperator(a.Op),
Value: uint(a.Value),
})
}
return seccomp.Args{sa}
}

func seccompOperator(o configs.Operator) seccomp.Operator {
switch o {
case configs.EqualTo:
return seccomp.EqualTo
case configs.NotEqualTo:
return seccomp.NotEqualTo
case configs.GreatherThan:
return seccomp.GreatherThan
case configs.LessThan:
return seccomp.LessThan
case configs.MaskEqualTo:
return seccomp.MaskEqualTo
}
return 0
}
24 changes: 0 additions & 24 deletions integration/exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -792,27 +792,3 @@ func TestSystemProperties(t *testing.T) {
t.Fatalf("kernel.shmmni property expected to be 8192, but is %s", shmmniOutput)
}
}

func TestSeccompNoChown(t *testing.T) {
if testing.Short() {
return
}
rootfs, err := newRootfs()
if err != nil {
t.Fatal(err)
}
defer remove(rootfs)
config := newTemplateConfig(rootfs)
config.Seccomp = &configs.Seccomp{}
config.Seccomp.Syscalls = append(config.Seccomp.Syscalls, &configs.Syscall{
Value: syscall.SYS_CHOWN,
Action: configs.Action(syscall.EPERM),
})
buffers, _, err := runContainer(config, "", "/bin/sh", "-c", "chown 1:1 /tmp")
if err == nil {
t.Fatal("running chown in a container should fail")
}
if s := buffers.String(); !strings.Contains(s, "not permitted") {
t.Fatalf("running chown should result in an EPERM but got %q", s)
}
}
208 changes: 208 additions & 0 deletions integration/seccomp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
// +build seccomp,linux,cgo

package integration

import (
"strings"
"syscall"
"testing"

"github.com/docker/libcontainer"
"github.com/docker/libcontainer/security/seccomp"
)

func TestSeccompDenyGetcwd(t *testing.T) {
if testing.Short() {
return
}

rootfs, err := newRootfs()
if err != nil {
t.Fatal(err)
}
defer remove(rootfs)

config := newTemplateConfig(rootfs)
config.SeccompConfig = &seccomp.Config{
Enable: true,
WhitelistToggle: false,
Syscalls: []*seccomp.BlockedSyscall{
{
Name: "getcwd",
},
},
}

container, err := newContainer(config)
if err != nil {
t.Fatal(err)
}
defer container.Destroy()

buffers := newStdBuffers()
pwd := &libcontainer.Process{
Args: []string{"pwd"},
Env: standardEnvironment,
Stdin: buffers.Stdin,
Stdout: buffers.Stdout,
Stderr: buffers.Stderr,
}

err = container.Start(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: Operation not permitted"
actual := strings.Trim(buffers.Stderr.String(), "\n")
if actual != expected {
t.Fatalf("Expected output %s but got %s\n", expected, actual)
}
}

func TestSeccompPermitWriteConditional(t *testing.T) {
if testing.Short() {
return
}

rootfs, err := newRootfs()
if err != nil {
t.Fatal(err)
}
defer remove(rootfs)

config := newTemplateConfig(rootfs)
config.SeccompConfig = &seccomp.Config{
Enable: true,
WhitelistToggle: false,
Syscalls: []*seccomp.BlockedSyscall{
{
Name: "write",
Conditions: []seccomp.SyscallCondition{
{
Argument: 0,
Operator: ">",
ValueOne: 1,
},
},
},
},
}

container, err := newContainer(config)
if err != nil {
t.Fatal(err)
}
defer container.Destroy()

buffers := newStdBuffers()
dmesg := &libcontainer.Process{
Args: []string{"busybox", "ls", "/"},
Env: standardEnvironment,
Stdin: buffers.Stdin,
Stdout: buffers.Stdout,
Stderr: buffers.Stderr,
}

err = container.Start(dmesg)
if err != nil {
t.Fatal(err)
}
if _, err := dmesg.Wait(); err != nil {
t.Fatalf("%s: %s", err, buffers.Stderr)
}
}

func TestSeccompDenyWriteConditional(t *testing.T) {
if testing.Short() {
return
}

rootfs, err := newRootfs()
if err != nil {
t.Fatal(err)
}
defer remove(rootfs)

config := newTemplateConfig(rootfs)
config.SeccompConfig = &seccomp.Config{
Enable: true,
WhitelistToggle: false,
Syscalls: []*seccomp.BlockedSyscall{
{
Name: "write",
Conditions: []seccomp.SyscallCondition{
{
Argument: 0,
Operator: ">",
ValueOne: 1,
},
},
},
},
}

container, err := newContainer(config)
if err != nil {
t.Fatal(err)
}
defer container.Destroy()

buffers := newStdBuffers()
dmesg := &libcontainer.Process{
Args: []string{"busybox", "ls", "does_not_exist"},
Env: standardEnvironment,
Stdin: buffers.Stdin,
Stdout: buffers.Stdout,
Stderr: buffers.Stderr,
}

err = container.Start(dmesg)
if err != nil {
t.Fatal(err)
}

ps, err := dmesg.Wait()
if err == nil {
t.Fatal("Expecting negative return, instead got 0!")
}

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("Busybox should fail with negative exit code, instead got %d!", exitCode)
}

// We're denying write to stderr, so we expect an empty buffer
expected := ""
actual := strings.Trim(buffers.Stderr.String(), "\n")
if actual != expected {
t.Fatalf("Expected output %s but got %s\n", expected, actual)
}
}
Loading