Add support to load a seccomp filter

This commit is contained in:
criyle 2020-12-13 21:08:52 -08:00
parent e3111eef82
commit 04cfb36722
11 changed files with 252 additions and 8 deletions

View File

@ -68,6 +68,10 @@ The `executorserver` need root privilege to create `cgroup`. Either creates sub-
- for example, by default container 0 will run with 10001 uid & gid and container 1 will run with 10002 uid & gid...
- `-enable-cpu-rate` enabled `cpu` cgroup to control cpu rate using cfs_quota & cfs_period control
- `-cpu-cfs-period` specifies cfs_period if cpu rate is enabled (default 100ms) (valid value: \[1ms, 1s\])
- `-seccomp-conf` specifies `seecomp` filter setting to load when running program
- for example, by `strace -c prog` to get all `syscall` needed and restrict to that sub set
- however, the `syscall` count in one platform(e.g. x86_64) is not suitable for all platform, so this option is not recommended
- the program killed by seccomp filter will have status `Dangerous Syscall`
#### Environment Variables

View File

@ -15,7 +15,8 @@ type Config struct {
PreFork int `flagUsage:"control # of the prefork workers"`
TmpFsParam string `flagUsage:"tmpfs mount data (only for default mount with no mount.yaml)" default:"size=16m,nr_inodes=4k"`
NetShare bool `flagUsage:"share net namespace with host"`
MountConf string `flagUsage:"specifics mount configuration file" default:"mount.yaml"`
MountConf string `flagUsage:"specifies mount configuration file" default:"mount.yaml"`
SeccompConf string `flagUsage:"specifies seccomp filter" default:"seccomp.yaml"`
Parallelism int `flagUsage:"control the # of concurrency execution" default:"4"`
CgroupPrefix string `flagUsage:"control cgroup prefix" default:"executor_server"`
ContainerCredStart int `flagUsage:"control the start uid&gid for container" default:"10000"`

View File

@ -79,6 +79,7 @@ func main() {
ContainerCredStart: conf.ContainerCredStart,
EnableCPURate: conf.EnableCPURate,
CPUCfsPeriod: conf.CPUCfsPeriod,
SeccompConf: conf.SeccompConf,
Logger: logger.Sugar(),
})
if err != nil {

1
env/config.go vendored
View File

@ -16,6 +16,7 @@ type Config struct {
TmpFsParam string
NetShare bool
MountConf string
SeccompConf string
CgroupPrefix string
Cpuset string
ContainerCredStart int

18
env/env_linux.go vendored
View File

@ -1,6 +1,7 @@
package env
import (
"fmt"
"io/ioutil"
"os"
"sync/atomic"
@ -45,6 +46,14 @@ func NewBuilder(c Config) (pool.EnvBuilder, error) {
m := mb.FilterNotExist().Mounts
c.Info("Created container mount at:", mb)
seccomp, err := readSeccompConf(c.SeccompConf)
if err != nil {
return nil, fmt.Errorf("failed to load seccomp config: %v", err)
}
if seccomp != nil {
c.Info("Load seccomp filter: ", c.SeccompConf)
}
unshareFlags := uintptr(forkexec.UnshareFlags)
if c.NetShare {
unshareFlags ^= syscall.CLONE_NEWNET
@ -111,7 +120,14 @@ func NewBuilder(c Config) (pool.EnvBuilder, error) {
if cgb != nil {
cgroupPool = pool.NewFakeCgroupPool(cgb, c.CPUCfsPeriod)
}
return pool.NewEnvBuilder(b, cgroupPool, workDir, c.Cpuset, c.EnableCPURate), nil
return pool.NewEnvBuilder(pool.Config{
Builder: b,
CgroupPool: cgroupPool,
WorkDir: workDir,
Cpuset: c.Cpuset,
CPURate: c.EnableCPURate,
Seccomp: seccomp,
}), nil
}
type credGen struct {

47
env/seccomp_linux.go vendored Normal file
View File

@ -0,0 +1,47 @@
package env
import (
"os"
"syscall"
"github.com/elastic/go-seccomp-bpf"
"github.com/elastic/go-ucfg/yaml"
"golang.org/x/net/bpf"
)
func readSeccompConf(name string) ([]syscall.SockFilter, error) {
conf, err := yaml.NewConfigWithFile(name)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, err
}
var policy seccomp.Policy
if err := conf.Unpack(&policy); err != nil {
return nil, err
}
inst, err := policy.Assemble()
if err != nil {
return nil, err
}
rawInst, err := bpf.Assemble(inst)
if err != nil {
return nil, err
}
return toSockFilter(rawInst), nil
}
func toSockFilter(raw []bpf.RawInstruction) []syscall.SockFilter {
filter := make([]syscall.SockFilter, 0, len(raw))
for _, instruction := range raw {
filter = append(filter, syscall.SockFilter{
Code: instruction.Op,
Jt: instruction.Jt,
Jf: instruction.Jf,
K: instruction.K,
})
}
return filter
}

3
go.mod
View File

@ -6,6 +6,8 @@ require (
cloud.google.com/go v0.74.0 // indirect
github.com/creack/pty v1.1.11
github.com/criyle/go-sandbox v0.5.8
github.com/elastic/go-seccomp-bpf v1.1.0
github.com/elastic/go-ucfg v0.7.0
github.com/fatih/camelcase v1.0.0 // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/gin-contrib/pprof v1.3.0
@ -26,6 +28,7 @@ require (
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.16.0
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
golang.org/x/sys v0.0.0-20201211090839-8ad439b19e0f
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect

2
go.sum
View File

@ -96,7 +96,9 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elastic/go-seccomp-bpf v1.1.0 h1:jUzzDc6LyCtdolZdvL/26dad6rZ9vsc7xZ2eadKECAU=
github.com/elastic/go-seccomp-bpf v1.1.0/go.mod h1:l+89Vy5BzjVcaX8USZRMOwmwwDScE+vxCFzzvQwN7T8=
github.com/elastic/go-ucfg v0.7.0 h1:1+C/sZdJKww8hKl7XtLPTjs4cFslhQF2fazKTF+ZE+4=
github.com/elastic/go-ucfg v0.7.0/go.mod h1:iaiY0NBIYeasNgycLyTvhJftQlQEUO2hpF+FX0JKxzo=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=

View File

@ -7,22 +7,34 @@ import (
"github.com/criyle/go-sandbox/container"
)
// Config specifies configuration to build environment builder
type Config struct {
Builder EnvironmentBuilder
CgroupPool CgroupPool
WorkDir string
Seccomp []syscall.SockFilter
Cpuset string
CPURate bool
}
type environmentBuilder struct {
builder EnvironmentBuilder
cgPool CgroupPool
workDir string
seccomp []syscall.SockFilter
cpuset string
cpuRate bool
}
// NewEnvBuilder creates builder for linux container pools
func NewEnvBuilder(builder EnvironmentBuilder, cgPool CgroupPool, workDir, cpuset string, cpuRate bool) EnvBuilder {
func NewEnvBuilder(c Config) EnvBuilder {
return &environmentBuilder{
builder: builder,
cgPool: cgPool,
workDir: workDir,
cpuset: cpuset,
cpuRate: cpuRate,
builder: c.Builder,
cgPool: c.CgroupPool,
workDir: c.WorkDir,
seccomp: c.Seccomp,
cpuset: c.Cpuset,
cpuRate: c.CPURate,
}
}
@ -46,5 +58,6 @@ func (b *environmentBuilder) Build() (Environment, error) {
wd: wd[0],
cpuset: b.cpuset,
cpuRate: b.cpuRate,
seccomp: b.seccomp,
}, nil
}

View File

@ -20,6 +20,7 @@ type environ struct {
cgPool CgroupPool
wd *os.File // container work dir
cpuset string
seccomp []syscall.SockFilter
cpuRate bool
}
@ -70,6 +71,7 @@ func (c *environ) Execve(ctx context.Context, param envexec.ExecveParam) (envexe
CTTY: param.TTY,
ExecFile: param.ExecFile,
RLimits: rLimits.PrepareRLimit(),
Seccomp: c.seccomp,
SyncFunc: syncFunc,
}
rt := c.Environment.Execve(ctx, p)

154
seccomp_example.yaml Normal file
View File

@ -0,0 +1,154 @@
# This is an example seccomp policy for x86_64 that whitelist regular syscalls and prevents network syscalls.
# The example is not ready for production use now, should be tuned for specific usages
# The default action is applied if none of the syscalls match.
#
# Possible actions:
# - kill_process (since Linux 4.14)
# - kill_thread
# - trap
# - errno (returns EPERM)
# - trace
# - log (since Linux 4.14)
# - allow
default_action: kill_process
syscalls:
- action: allow
names:
# file actions
- read
- write
- readv
- writev
- close
- fstat
- lseek
- dup
- dup2
- dup3
- ioctl
- fcntl
- fadvise64
- pipe
- pread64
- pwrite64
# memory action
- mmap
- mprotect
- munmap
- brk
- mremap
- msync
- mincore
- madvise
# signal
- rt_sigaction
- rt_sigprocmask
- rt_sigreturn
- rt_sigpending
- sigaltstack
# work dir
- getcwd
# exit
- exit
- exit_group
# others
- arch_prctl
- restart_syscall
# time
- gettimeofday
- times
- time
- clock_gettime
- clock_getres
# rlimit
- getrlimit
- getrusage
- setrlimit
# execve
- execve
- execveat
# open
- open
- openat
# delete
- unlink
- unlinkat
# link
- readlink
- readlinkat
# permission check
- lstat
- stat
- access
- faccessat
# process
- clone
- fork
- vfork
- nanosleep
- wait4
- getpid
- gettid
# lock
- futex
# other
- getdents
- getdents64
- sysinfo
- set_tid_address
- set_robust_list
- getrandom
- umask
- rename
- chmod
- mkdir
- chdir
- fchdir
- ftruncate
- sched_getaffinity
- sched_yield
- uname
- prlimit64
- fchmodat
- poll
# user
- getuid
- geteuid
- getgid
- getegid
- action: errno
names:
- connect
- accept
- sendto
- recvfrom
- sendmsg
- recvmsg
- bind
- listen
- setsockopt
- socketpair
- getsockopt