Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/seccompagent
/seccompshell
*.swp
13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,17 @@ IMAGE_TAG=$(shell ./tools/image-tag)
IMAGE_BRANCH_TAG=$(shell ./tools/image-tag branch)
CONTAINER_REPO ?= quay.io/kinvolk/seccompagent

.PHONY: all
all: seccompagent seccompshell

.PHONY: seccompagent
seccompagent:
$(GO_BUILD) -o seccompagent ./cmd/seccompagent

.PHONY: seccompshell
seccompshell:
$(GO_BUILD) -tags seccomp -o seccompshell ./cmd/seccompshell

.PHONY: container-build
container-build:
docker build -t $(CONTAINER_REPO):$(IMAGE_TAG) -f Dockerfile .
Expand All @@ -28,3 +35,9 @@ vendor:
.PHONY: test
test:
go test -test.v ./...

.PHONY: local-containerd-install
local-containerd-install:
docker build -t local-seccomp-agent .
docker save --output local-seccomp-agent.tar local-seccomp-agent
sudo ctr --address /run/customcontainerd/containerd.sock --namespace k8s.io images import local-seccomp-agent.tar
48 changes: 34 additions & 14 deletions cmd/seccompagent/seccompagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/kinvolk/seccompagent/pkg/handlers"
"github.com/kinvolk/seccompagent/pkg/kuberesolver"
"github.com/kinvolk/seccompagent/pkg/nsenter"
"github.com/kinvolk/seccompagent/pkg/opa"
"github.com/kinvolk/seccompagent/pkg/registry"

"github.com/opencontainers/runtime-spec/specs-go"
Expand All @@ -30,7 +31,7 @@ var (

func init() {
flag.StringVar(&socketFile, "socketfile", "/run/seccomp-agent.socket", "Socket file")
flag.StringVar(&resolverParam, "resolver", "", "Container resolver to use [none, demo-basic, kubernetes]")
flag.StringVar(&resolverParam, "resolver", "", "Container resolver to use [none, demo-basic, kubernetes, opa]")
flag.StringVar(&logflags, "log", "info", "log level [trace,debug,info,warn,error,fatal,color,nocolor,json]")
}

Expand Down Expand Up @@ -69,47 +70,49 @@ func main() {
// Kubernetes API, find the pod, and allow or deny a syscall depending
// on the pod specifications (e.g. namespace, annotations,
// serviceAccount).
resolver = func(state *specs.ContainerProcessState) *registry.Registry {
r := registry.New()
resolver = func(state *specs.ContainerProcessState) registry.Filter {
f := registry.NewSimpleFilter()

// Example:
// / # mount -t proc proc root
// / # ls /root/self/cmdline
// /root/self/cmdline
allowedFilesystems := map[string]struct{}{"proc": struct{}{}}
r.Add("mount", handlers.Mount(allowedFilesystems))
f.AddHandler("mount", handlers.Mount(allowedFilesystems))

// Example:
// # chmod 777 /
// chmod: /: Bad message
r.Add("chmod", handlers.Error(unix.EBADMSG))
f.AddHandler("chmod", handlers.Error(unix.EBADMSG))

// Example:
// # mkdir /abc
// # ls -d /abc*
// /abc-pid-3528098
if state != nil {
r.Add("mkdir", handlers.MkdirWithSuffix(fmt.Sprintf("-pid-%d", state.State.Pid)))
f.AddHandler("mkdir", handlers.MkdirWithSuffix(fmt.Sprintf("-pid-%d", state.State.Pid)))
}

return r
return f
}
case "kubernetes":
kubeResolverFunc := func(podCtx *kuberesolver.PodContext, metadata map[string]string) *registry.Registry {
kubeResolverFunc := func(podCtx *kuberesolver.PodContext, metadata map[string]string) registry.Filter {
log.WithFields(log.Fields{
"pod": podCtx,
"metadata": metadata,
}).Debug("New container")

r := registry.New()
f := registry.NewSimpleFilter()

f.AddHandler("chmod", handlers.ErrorSeq())

if v, ok := metadata["MKDIR_TMPL"]; ok {
tmpl, err := template.New("mkdirTmpl").Parse(v)
if err == nil {
var suffix strings.Builder
err = tmpl.Execute(&suffix, podCtx)
if err == nil {
r.Add("mkdir", handlers.MkdirWithSuffix(suffix.String()))
f.AddHandler("mkdir", handlers.MkdirWithSuffix(suffix.String()))
}
}
}
Expand All @@ -118,15 +121,15 @@ func main() {
d, ok := metadata["EXEC_DURATION"]
if ok {
duration, _ := time.ParseDuration(d)
r.Add("execve", handlers.ExecCondition(fileName, duration))
f.AddHandler("execve", handlers.ExecCondition(fileName, duration))
}
}

if sidecars, ok := metadata["SIDECARS"]; ok {
d, ok := metadata["SIDECARS_DELAY"]
if ok {
duration, _ := time.ParseDuration(d)
r.Add("execve", handlers.ExecSidecars(podCtx, sidecars, duration))
f.AddHandler("execve", handlers.ExecSidecars(podCtx, sidecars, duration))
}
}

Expand All @@ -138,15 +141,32 @@ func main() {
allowedFilesystems["sysfs"] = struct{}{}
}
if len(allowedFilesystems) > 0 {
r.Add("mount", handlers.Mount(allowedFilesystems))
f.AddHandler("mount", handlers.Mount(allowedFilesystems))
}
return r
return f
}
var err error
resolver, err = kuberesolver.KubeResolver(kubeResolverFunc)
if err != nil {
panic(err)
}
case "opa":
kubeResolverFunc := func(podCtx *kuberesolver.PodContext, metadata map[string]string) registry.Filter {
log.WithFields(log.Fields{
"pod": podCtx,
"metadata": metadata,
}).Debug("New container")

f := opa.NewOpaFilter(podCtx)

return f
}
var err error
resolver, err = kuberesolver.KubeResolver(kubeResolverFunc)
if err != nil {
panic(err)
}

default:
panic(errors.New("invalid container resolver"))
}
Expand Down
129 changes: 129 additions & 0 deletions cmd/seccompshell/seccompshell.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// +build linux,cgo

package main

import (
"encoding/json"
"errors"
"flag"
"fmt"
"io/ioutil"
"net"
"os"
"strings"

"github.com/opencontainers/runc/libcontainer/seccomp"
"github.com/opencontainers/runc/libcontainer/specconv"
"github.com/opencontainers/runc/libcontainer/utils"
"github.com/opencontainers/runtime-spec/specs-go"
log "github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)

var (
paramSeccompFile string
socketFile string
paramMetadata string
paramID string
logflags string
)

func init() {
flag.StringVar(&paramSeccompFile, "seccomp-policy", "/var/lib/kubelet/seccomp/default.json", "Seccomp Policy file")
flag.StringVar(&socketFile, "socketfile", "/run/seccomp-agent.socket", "Socket file")
flag.StringVar(&paramMetadata, "metadata", "", "Metadata to send to the seccomp agent")
flag.StringVar(&paramID, "id", "", "Container ID to send to the seccomp agent")
flag.StringVar(&logflags, "log", "info", "log level [trace,debug,info,warn,error,fatal,color,nocolor,json]")
}

func sendContainerProcessState(listenerPath string, state *specs.ContainerProcessState, fds ...int) error {
conn, err := net.Dial("unix", listenerPath)
if err != nil {
return fmt.Errorf("cannot connect to %q: %v\n", listenerPath, err)
}

socket, err := conn.(*net.UnixConn).File()
if err != nil {
return fmt.Errorf("cannot get socket: %v\n", err)
}
defer socket.Close()

b, err := json.Marshal(state)
if err != nil {
return fmt.Errorf("cannot marshall seccomp state: %v\n", err)
}

err = utils.SendFds(socket, b, fds...)
if err != nil {
return fmt.Errorf("cannot send seccomp fd to %s: %v\n", listenerPath, err)
}

return nil
}

func main() {
flag.Parse()
for _, v := range strings.Split(logflags, ",") {
if v == "json" {
log.SetFormatter(&log.JSONFormatter{})
} else if v == "color" {
log.SetFormatter(&log.TextFormatter{ForceColors: true})
} else if v == "nocolor" {
log.SetFormatter(&log.TextFormatter{DisableColors: true})
} else if lvl, err := log.ParseLevel(v); err == nil {
log.SetLevel(lvl)
} else {
fmt.Fprintf(os.Stderr, "Invalid log level: %s\n", err.Error())
flag.Usage()
os.Exit(1)
}
}
if flag.NArg() == 0 {
panic(errors.New("invalid command"))
}

buf, err := ioutil.ReadFile(paramSeccompFile)
if err != nil {
panic(fmt.Errorf("cannot read file %q: %s", paramSeccompFile, err))
}

seccompConfigOCI := &specs.LinuxSeccomp{}
json.Unmarshal(buf, seccompConfigOCI)

seccompConfig, err := specconv.SetupSeccomp(seccompConfigOCI)
if err != nil {
panic(fmt.Errorf("cannot convert seccomp policy from OCI format to libcontainer format: %s", err))
}

if err := unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil {
panic(fmt.Errorf("cannot set nonewprivileges", err))
}

containerProcessState := &specs.ContainerProcessState{
Version: specs.Version,
FdIndexes: map[specs.FdIndexKey]int{specs.SeccompFdIndexKey: 0},
Pid: os.Getpid(),
Metadata: paramMetadata,
State: specs.State{
Version: specs.Version,
ID: paramID,
Status: specs.StateRunning,
Pid: os.Getpid(),
Bundle: "",
Annotations: map[string]string{},
},
}
seccompFd, err := seccomp.InitSeccomp(seccompConfig)
if err != nil || seccompFd == -1 {
panic(fmt.Errorf("cannot init seccomp: %s", err))
}

if err := sendContainerProcessState(socketFile,
containerProcessState, int(seccompFd)); err != nil {
panic(fmt.Errorf("cannot send message to seccomp agent: %s", err))
}

if err := unix.Exec(flag.Arg(0), flag.Args()[1:], os.Environ()); err != nil {
panic(fmt.Errorf("cannot exec command", err))
}
}
Loading