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

Commit

Permalink
Introduce support for syscall filtering in containers #237
Browse files Browse the repository at this point in the history
This PR introduces the ability to filter system calls on a per-container basis on Linux, using libseccomp to support multiple architectures.

This adds another layer of security between containers and the kernel. System calls which are unnecessary in a container or problematic from a security perspective can be restricted to prevent their use. Most of the truly problematic syscalls are already restricted by dropping capabilities; this adds an additional, finer-grained layer of protection.

This PR adds a vendored library dependency (Go bindings for libseccomp) and a build dependency on libseccomp >= v2.1. The actual changes to libcontainer are fairly minimal, most of the delta is in the libseccomp bindings.

Docker-DCO-1.1-Signed-off-by: Dan Walsh <[email protected]> (github: rhatdan)
Docker-DCO-1.1-Signed-off-by: Matt Heon <[email protected]> (github: mheon)
  • Loading branch information
mheon committed Feb 17, 2015
1 parent 3f35b26 commit 09f2735
Show file tree
Hide file tree
Showing 16 changed files with 2,534 additions and 6 deletions.
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
FROM golang:1.4

RUN apt-get update && apt-get install -y gcc make libseccomp2 libseccomp-dev
RUN go get golang.org/x/tools/cmd/cover

ENV GOPATH $GOPATH:/go/src/github.com/docker/libcontainer/vendor
Expand All @@ -17,7 +18,7 @@ WORKDIR /go/src/github.com/docker/libcontainer
RUN cp sample_configs/minimal.json /busybox/container.json

RUN go get -d -v ./...
RUN make direct-install
RUN TEST_TAGS="-tags seccomp" make direct-install

ENTRYPOINT ["/dind"]
CMD ["make", "direct-test"]
CMD ["make", "TEST_TAGS=-tags seccomp", "direct-test"]
5 changes: 2 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

all:
docker build -t dockercore/libcontainer .

Expand All @@ -18,7 +17,7 @@ direct-test-short:
go test $(TEST_TAGS) -cover -test.short -v $(GO_PACKAGES)

direct-build:
go build -v $(GO_PACKAGES)
go build $(TEST_TAGS) -v $(GO_PACKAGES)

direct-install:
go install -v $(GO_PACKAGES)
go install $(TEST_TAGS) -v $(GO_PACKAGES)
5 changes: 5 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/docker/libcontainer/cgroups"
"github.com/docker/libcontainer/mount"
"github.com/docker/libcontainer/network"
"github.com/docker/libcontainer/security/seccomp"
)

type MountConfig mount.MountConfig
Expand Down Expand Up @@ -124,11 +125,15 @@ type Config struct {
// AdditionalGroups specifies the gids that should be added to supplementary groups
// in addition to those that the user belongs to.
AdditionalGroups []int `json:"additional_groups,omitempty"`

// UidMappings is an array of User ID mappings for User Namespaces
UidMappings []IDMap `json:"uid_mappings,omitempty"`

// GidMappings is an array of Group ID mappings for User Namespaces
GidMappings []IDMap `json:"gid_mappings,omitempty"`

// SeccompConfig holds information on system calls to be restricted in the container
SeccompConfig seccomp.SeccompConfig `json:"seccomp_config,omitempty"`
}

// Routes can be specified to create entries in the route table as the container is started
Expand Down
92 changes: 92 additions & 0 deletions integration/seccomp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// +build seccomp,linux,cgo

package integration

import (
"strings"
"testing"

"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.SeccompConfig{
Enable: true,
Whitelist: false,
Syscalls: []seccomp.BlockedSyscall{
{
Name: "getcwd",
},
},
}

buffers2, _, err := runContainer(config, "", "pwd")
if err != nil {
t.Fatal(err)
}
t.Logf("Buffer is %s", buffers2.Stdout.String())

buffers, exitCode, err := runContainer(config, "", "pwd")
if err != nil {
t.Fatal(err)
}

if exitCode != 1 {
t.Fatalf("Getcwd should fail with exit code 1, 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 TestSeccompDenyMmap(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.SeccompConfig{
Enable: true,
Whitelist: false,
Syscalls: []seccomp.BlockedSyscall{
{
Name: "mmap",
},
},
}

buffers, exitCode, err := runContainer(config, "", "echo", "hello world")
if err != nil {
t.Fatal(err)
}

if exitCode != 20 {
t.Fatalf("Busybox should fail to start with exit code 20, instead got %d!", exitCode)
}

expected := "mmap of a spare page failed!"
actual := strings.Trim(buffers.Stderr.String(), "\n")
if actual != expected {
t.Fatalf("Expected output %s but got %s\n", expected, actual)
}
}
2 changes: 1 addition & 1 deletion label/label_selinux_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// +build selinux,linux
// +build selinux,linux,cgo

package label

Expand Down
5 changes: 5 additions & 0 deletions namespaces/execin.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/docker/libcontainer/cgroups"
"github.com/docker/libcontainer/label"
"github.com/docker/libcontainer/mount"
"github.com/docker/libcontainer/security/seccomp"
"github.com/docker/libcontainer/system"
"github.com/docker/libcontainer/utils"
)
Expand Down Expand Up @@ -108,6 +109,10 @@ func FinalizeSetns(container *libcontainer.Config, args []string) error {
return fmt.Errorf("setup rlimits %s", err)
}

if err := seccomp.InitSeccomp(container.SeccompConfig); err != nil {
return fmt.Errorf("initializing seccomp %s", err)
}

if err := FinalizeNamespace(container); err != nil {
return err
}
Expand Down
9 changes: 9 additions & 0 deletions namespaces/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/docker/libcontainer/network"
"github.com/docker/libcontainer/security/capabilities"
"github.com/docker/libcontainer/security/restrict"
"github.com/docker/libcontainer/security/seccomp"
"github.com/docker/libcontainer/system"
"github.com/docker/libcontainer/user"
"github.com/docker/libcontainer/utils"
Expand Down Expand Up @@ -157,6 +158,10 @@ func initDefault(container *libcontainer.Config, uncleanRootfs, consolePath stri
return fmt.Errorf("get parent death signal %s", err)
}

if err := seccomp.InitSeccomp(container.SeccompConfig); err != nil {
return fmt.Errorf("initializing seccomp %s", err)
}

if err := FinalizeNamespace(container); err != nil {
return fmt.Errorf("finalize namespace %s", err)
}
Expand Down Expand Up @@ -241,6 +246,10 @@ func initUserNs(container *libcontainer.Config, uncleanRootfs, consolePath strin
return fmt.Errorf("get parent death signal %s", err)
}

if err := seccomp.InitSeccomp(container.SeccompConfig); err != nil {
return fmt.Errorf("initializing seccomp %s", err)
}

if err := FinalizeNamespace(container); err != nil {
return fmt.Errorf("finalize namespace %s", err)
}
Expand Down
Loading

0 comments on commit 09f2735

Please sign in to comment.