From 42e7c988f926a7e4e0403e722c2231c69a679eb2 Mon Sep 17 00:00:00 2001 From: Andrew Kroh Date: Fri, 11 May 2018 11:26:13 -0400 Subject: [PATCH] Add seccomp whitelist policy This PR adds to the seccomp enhancement in #6832 by adding a whitelist policy. The policies are derived from a combination of static analysis of the object code and runtime profiling (to see what the cgo linked libraries are doing). The policies also account for the system calls used while running the system tests with coverage and race detection. Closes #5213 --- CHANGELOG.asciidoc | 4 +- auditbeat/auditbeat.reference.yml | 14 +- auditbeat/cmd/root.go | 8 +- auditbeat/include/list.go | 8 ++ filebeat/filebeat.reference.yml | 14 +- heartbeat/heartbeat.reference.yml | 14 +- libbeat/_meta/config.reference.yml | 14 +- libbeat/cmd/instance/beat.go | 57 +------- libbeat/common/seccomp/README.md | 112 ++++++++++++++++ libbeat/common/seccomp/policy.go.tpl | 27 ++++ libbeat/common/seccomp/policy_linux_386.go | 109 ++++++++++++++++ libbeat/common/seccomp/policy_linux_amd64.go | 122 ++++++++++++++++++ libbeat/common/seccomp/policy_linux_arm.go | 20 +++ .../common/seccomp/seccomp-profiler-allow.txt | 26 ++++ .../seccomp/seccomp-profiler-blacklist.txt | 31 +++++ libbeat/common/seccomp/seccomp.go | 113 ++++++++++++++++ libbeat/docs/security/linux-seccomp.asciidoc | 25 ++-- libbeat/scripts/Makefile | 23 ++++ metricbeat/metricbeat.reference.yml | 14 +- packetbeat/packetbeat.reference.yml | 14 +- winlogbeat/winlogbeat.reference.yml | 14 +- 21 files changed, 624 insertions(+), 159 deletions(-) create mode 100644 auditbeat/include/list.go create mode 100644 libbeat/common/seccomp/README.md create mode 100644 libbeat/common/seccomp/policy.go.tpl create mode 100644 libbeat/common/seccomp/policy_linux_386.go create mode 100644 libbeat/common/seccomp/policy_linux_amd64.go create mode 100644 libbeat/common/seccomp/policy_linux_arm.go create mode 100644 libbeat/common/seccomp/seccomp-profiler-allow.txt create mode 100644 libbeat/common/seccomp/seccomp-profiler-blacklist.txt create mode 100644 libbeat/common/seccomp/seccomp.go diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 5db291cebd4..9726eba787a 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -127,8 +127,8 @@ https://github.com/elastic/beats/compare/v6.2.3...master[Check the HEAD diff] - Do not log errors if X-Pack Monitoring is enabled but Elastisearch X-Pack is not. {pull}6627[6627] - Add rename processor. {pull}6292[6292] - Add IP-addresses and MAC-addresses to add_host_metadata. {pull}6878[6878] -- Add a default seccomp (secure computing) filter on Linux that prohibits - execve, execveat, fork, and vfork syscalls. A custom policy can be configured. {issue}5213[5213] +- Added a seccomp (secure computing) filter on Linux that whitelists the + necessary system calls used by each Beat. {issue}5213[5213] - Update Sarama to v1.16.0, adding better support for kafka 0.11, 1.0, and 1.1 {pull}7025[7025] - Ship fields.yml as part of the binary {pull}4834[4834] - Added options to dev-tools/cmd/dashboards/export_dashboard.go: -indexPattern to include index-pattern in output, -quiet to be quiet. {pull}7101[7101] diff --git a/auditbeat/auditbeat.reference.yml b/auditbeat/auditbeat.reference.yml index 981f197fb2e..ad69bfc967b 100644 --- a/auditbeat/auditbeat.reference.yml +++ b/auditbeat/auditbeat.reference.yml @@ -1088,16 +1088,4 @@ logging.files: #============================= Process Security ================================ # Enable or disable seccomp system call filtering on Linux. Default is enabled. -seccomp.enabled: true - -# The default seccomp policy. This policy blacklists system calls that allow -# process creation. -seccomp: - default_action: allow - syscalls: - - action: errno - names: - - execve - - execveat - - fork - - vfork +#seccomp.enabled: true diff --git a/auditbeat/cmd/root.go b/auditbeat/cmd/root.go index bb85f61e145..61b3a8c0482 100644 --- a/auditbeat/cmd/root.go +++ b/auditbeat/cmd/root.go @@ -3,11 +3,13 @@ package cmd import ( "github.com/spf13/pflag" - "github.com/elastic/beats/metricbeat/beater" - "github.com/elastic/beats/auditbeat/core" - cmd "github.com/elastic/beats/libbeat/cmd" + "github.com/elastic/beats/libbeat/cmd" + "github.com/elastic/beats/metricbeat/beater" "github.com/elastic/beats/metricbeat/mb/module" + + // Register includes. + _ "github.com/elastic/beats/auditbeat/include" ) // Name of the beat (auditbeat). diff --git a/auditbeat/include/list.go b/auditbeat/include/list.go new file mode 100644 index 00000000000..0dc097b4ac2 --- /dev/null +++ b/auditbeat/include/list.go @@ -0,0 +1,8 @@ +package include + +import ( + // Include all Auditbeat modules so that they register their + // factories with the global registry. + _ "github.com/elastic/beats/auditbeat/module/auditd" + _ "github.com/elastic/beats/auditbeat/module/file_integrity" +) diff --git a/filebeat/filebeat.reference.yml b/filebeat/filebeat.reference.yml index 9161f68329b..a30b5b981fb 100644 --- a/filebeat/filebeat.reference.yml +++ b/filebeat/filebeat.reference.yml @@ -1644,16 +1644,4 @@ logging.files: #============================= Process Security ================================ # Enable or disable seccomp system call filtering on Linux. Default is enabled. -seccomp.enabled: true - -# The default seccomp policy. This policy blacklists system calls that allow -# process creation. -seccomp: - default_action: allow - syscalls: - - action: errno - names: - - execve - - execveat - - fork - - vfork +#seccomp.enabled: true diff --git a/heartbeat/heartbeat.reference.yml b/heartbeat/heartbeat.reference.yml index b2eee4f7c7a..df04a1dd559 100644 --- a/heartbeat/heartbeat.reference.yml +++ b/heartbeat/heartbeat.reference.yml @@ -1197,16 +1197,4 @@ logging.files: #============================= Process Security ================================ # Enable or disable seccomp system call filtering on Linux. Default is enabled. -seccomp.enabled: true - -# The default seccomp policy. This policy blacklists system calls that allow -# process creation. -seccomp: - default_action: allow - syscalls: - - action: errno - names: - - execve - - execveat - - fork - - vfork +#seccomp.enabled: true diff --git a/libbeat/_meta/config.reference.yml b/libbeat/_meta/config.reference.yml index 7cf928e0922..e190d2d013a 100644 --- a/libbeat/_meta/config.reference.yml +++ b/libbeat/_meta/config.reference.yml @@ -983,16 +983,4 @@ logging.files: #============================= Process Security ================================ # Enable or disable seccomp system call filtering on Linux. Default is enabled. -seccomp.enabled: true - -# The default seccomp policy. This policy blacklists system calls that allow -# process creation. -seccomp: - default_action: allow - syscalls: - - action: errno - names: - - execve - - execveat - - fork - - vfork +#seccomp.enabled: true diff --git a/libbeat/cmd/instance/beat.go b/libbeat/cmd/instance/beat.go index e70e401d5a1..1f5b4deb9d0 100644 --- a/libbeat/cmd/instance/beat.go +++ b/libbeat/cmd/instance/beat.go @@ -26,6 +26,7 @@ import ( "github.com/elastic/beats/libbeat/common" "github.com/elastic/beats/libbeat/common/cfgwarn" "github.com/elastic/beats/libbeat/common/file" + "github.com/elastic/beats/libbeat/common/seccomp" "github.com/elastic/beats/libbeat/dashboards" "github.com/elastic/beats/libbeat/keystore" "github.com/elastic/beats/libbeat/logp" @@ -40,7 +41,6 @@ import ( svc "github.com/elastic/beats/libbeat/service" "github.com/elastic/beats/libbeat/template" "github.com/elastic/beats/libbeat/version" - "github.com/elastic/go-seccomp-bpf" "github.com/elastic/go-sysinfo" "github.com/elastic/go-sysinfo/types" @@ -288,10 +288,8 @@ func (b *Beat) launch(bt beat.Creator) error { svc.BeforeRun() defer svc.Cleanup() - if b.Config.Seccomp == nil || b.Config.Seccomp.Enabled() { - if err = loadSeccompFilter(b.Config.Seccomp); err != nil { - return err - } + if err = seccomp.LoadFilter(b.Config.Seccomp); err != nil { + return err } beater, err := b.createBeater(bt) @@ -786,52 +784,3 @@ func logSystemInfo(info beat.Info) { } } } - -// loadSeccompFilter loads a seccomp system call filter into the kernel for -// this process. This feature is only available on Linux and our implementation -// requires Linux 3.17 or newer. This only returns an error if there is a -// configuration problem. An errors interfacing with the kernel are only logged. -func loadSeccompFilter(c *common.Config) error { - if runtime.GOOS != "linux" { - return nil - } - - filter := seccomp.Filter{ - NoNewPrivs: true, - Flag: seccomp.FilterFlagTSync, - Policy: seccomp.Policy{ - DefaultAction: seccomp.ActionAllow, - Syscalls: []seccomp.SyscallGroup{ - { - Action: seccomp.ActionErrno, - Names: []string{ - "execve", - "fork", - "vfork", - "execveat", - }, - }, - }, - }, - } - - if c != nil && (c.HasField("default_profile") || c.HasField("syscalls")) { - var p seccomp.Policy - if err := c.Unpack(&p); err != nil { - return err - } - filter.Policy = p - } - - log := logp.L().With("seccomp_filter", filter) - if err := seccomp.LoadFilter(filter); err != nil { - log.Warnf("seccomp BPF filter could not be installed (perhaps the "+ - "kernel doesn't support this feature): %v", err) - - // Only log the error. This is a non-fatal issue. - return nil - } - - log.Infow("seccomp BPF filter successfully installed") - return nil -} diff --git a/libbeat/common/seccomp/README.md b/libbeat/common/seccomp/README.md new file mode 100644 index 00000000000..a4b4db1b775 --- /dev/null +++ b/libbeat/common/seccomp/README.md @@ -0,0 +1,112 @@ +# Package seccomp + +Package seccomp loads a Linux seccomp BPF filter that controls what system calls +the process can use. Here is a description of the files in this directory. + +| File | Description | +|--------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| seccomp.go | Contains the code for registering application specific policies and for loading the seccomp BPF filter. | +| policy.go.tpl | This is a Go text/template used to generate code for a whitelist seccomp BPF policy. The template is used when running `make seccomp` and `make seccomp-package`. The tool that reads the template file is the [seccomp-profiler](https://github.com/elastic/go-seccomp-bpf/tree/master/cmd/seccomp-profiler). | +| seccomp-profiler-blacklist.txt | A list of system calls that are filtered from the resulting policy even if they are detected as being used in a binary. For example if the os/exec package is included by a dependency it will cause several system calls to be included such as execve, ptrace, and chroot. The system calls are never actually used so we want them removed from the profile. | +| seccomp-profiler-allow.txt | A list of system calls that are always included in the resulting policy. System calls that are made from CGO or linked libraries (such as libpthread) are not detected by seccomp-profiler and must be manually added to the list. Tools like `strace -c` and Auditbeat (where event.action=violated-seccomp-policy) can be used to find missing system calls. System calls from any architecture may be added to the list because it is automatically filtered based on architecture. | +| policy_linux_$arch.go | This is a default policy provided by libbeat. It is used when a Beat does not register its own application specific policy with `seccomp.MustRegisterPolicy()` (e.g. community Beats).| + +## Developing a Whitelist Policy + +Policy files can be generated for amd64 and 386 architectures (our tooling is +currently limited to these architectures). You can generate a policy by building +the packages and then profiling the resulting binaries. This ensures that the +profiles are built based on the binaries you plan to release. The policies are +stored at `$beatname/include/seccomp_linux_$goarch.go`. + +```sh +make package && make seccomp-package +``` + +If you are developing on Linux you can profile the binary produced by `make`. +It will only generate a profile for the current architecture. + +```sh +make && make seccomp +``` + +You should thoroughly test the policy as described below and add any additional +syscalls that are detected. + +The `include` package must be imported by the Beat such that the policy is +registered as a side-effect. + +```go +import ( + _ "github.com/user/beatname/include" +) +``` + +## Testing Seccomp Policies + +### Linux Auditing + +Seccomp violations are reported by Linux kernel's audit subsystem. So you can run +Auditbeat to look for system call denials. With this config Auditbeat will +report only seccomp events. + +```yaml +auditbeat.modules: +- module: auditd + processors: + - drop_event.when.not.equals.event.type: seccomp + +output.console.pretty: true + +logging.level: warning +``` + +Then start Auditbeat and it will log events to stdout. + +`sudo auditbeat -e` + +### strace + +Another profiling option is `strace -c`. This will record all system calls used +by the Beat. Then this list can be used to steer the development of a policy. + +```sh +sudo strace -c ./metricbeat -strict.perms=false +% time seconds usecs/call calls errors syscall +------ ----------- ----------- --------- --------- ---------------- + 79.75 0.002521 97 26 futex + 2.97 0.000094 3 34 mmap + 2.69 0.000085 1 120 rt_sigaction + 2.25 0.000071 18 4 open + 1.96 0.000062 12 5 1 openat + 1.68 0.000053 5 11 read + 1.46 0.000046 4 12 getrandom + 1.42 0.000045 4 11 mprotect + 1.08 0.000034 6 6 fstat + 0.85 0.000027 14 2 munmap + 0.70 0.000022 6 4 clone + 0.57 0.000018 18 1 ioctl + 0.47 0.000015 2 8 close + 0.41 0.000013 2 8 2 epoll_ctl + 0.38 0.000012 1 11 rt_sigprocmask + 0.22 0.000007 7 1 newfstatat + 0.19 0.000006 6 1 write + 0.16 0.000005 1 6 fcntl + 0.13 0.000004 1 3 brk + 0.13 0.000004 1 5 5 access + 0.13 0.000004 4 1 sched_getaffinity + 0.09 0.000003 2 2 sigaltstack + 0.06 0.000002 2 1 getcwd + 0.06 0.000002 2 1 getrlimit + 0.06 0.000002 2 1 arch_prctl + 0.06 0.000002 2 1 gettid + 0.03 0.000001 1 1 set_tid_address + 0.03 0.000001 1 1 set_robust_list + 0.00 0.000000 0 1 execve + 0.00 0.000000 0 1 getuid + 0.00 0.000000 0 1 getgid + 0.00 0.000000 0 1 readlinkat + 0.00 0.000000 0 1 epoll_create1 +------ ----------- ----------- --------- --------- ---------------- +100.00 0.003161 293 8 total +``` diff --git a/libbeat/common/seccomp/policy.go.tpl b/libbeat/common/seccomp/policy.go.tpl new file mode 100644 index 00000000000..6b9df750662 --- /dev/null +++ b/libbeat/common/seccomp/policy.go.tpl @@ -0,0 +1,27 @@ +// Code generated by seccomp-profiler - DO NOT EDIT. + +// {{ printf "+build linux,"}}{{.GOARCH}} + +package {{.Package}} + +import ( + "github.com/elastic/go-seccomp-bpf" + + beat "github.com/elastic/beats/libbeat/common/seccomp" +) + +func init() { + beat.MustRegisterPolicy(&seccomp.Policy{ + DefaultAction: seccomp.ActionErrno, + Syscalls: []seccomp.SyscallGroup{ + { + Action: seccomp.ActionAllow, + Names: []string{ +{{- range $syscall := .SyscallNames}} + "{{ $syscall }}", +{{- end}} + }, + }, + }, + }) +} diff --git a/libbeat/common/seccomp/policy_linux_386.go b/libbeat/common/seccomp/policy_linux_386.go new file mode 100644 index 00000000000..ca3a7aa10cc --- /dev/null +++ b/libbeat/common/seccomp/policy_linux_386.go @@ -0,0 +1,109 @@ +package seccomp + +import ( + "github.com/elastic/go-seccomp-bpf" +) + +func init() { + defaultPolicy = &seccomp.Policy{ + DefaultAction: seccomp.ActionErrno, + Syscalls: []seccomp.SyscallGroup{ + { + Action: seccomp.ActionAllow, + Names: []string{ + "_llseek", + "access", + "brk", + "clock_gettime", + "clone", + "close", + "dup", + "dup2", + "epoll_create", + "epoll_create1", + "epoll_ctl", + "epoll_wait", + "exit", + "exit_group", + "fchdir", + "fchmod", + "fchown32", + "fcntl", + "fdatasync", + "flock", + "fstat64", + "fsync", + "ftruncate64", + "futex", + "getcwd", + "getdents", + "getdents64", + "geteuid32", + "getgid32", + "getpid", + "getppid", + "getrandom", + "getrusage", + "gettid", + "gettimeofday", + "getuid32", + "inotify_add_watch", + "inotify_init1", + "inotify_rm_watch", + "ioctl", + "kill", + "lstat64", + "madvise", + "mincore", + "mkdirat", + "mmap2", + "mprotect", + "munmap", + "nanosleep", + "open", + "openat", + "pipe", + "pipe2", + "poll", + "pread64", + "pselect6", + "pwrite64", + "read", + "readlink", + "readlinkat", + "rename", + "renameat", + "restart_syscall", + "rt_sigaction", + "rt_sigprocmask", + "rt_sigreturn", + "sched_getaffinity", + "sched_yield", + "sendfile64", + "set_robust_list", + "set_thread_area", + "setgid32", + "setgroups32", + "setitimer", + "setuid32", + "sigaltstack", + "socketcall", + "stat", + "stat64", + "statfs64", + "sysinfo", + "tgkill", + "time", + "tkill", + "uname", + "unlink", + "unlinkat", + "wait4", + "waitid", + "write", + "writev", + }, + }, + }, + } +} diff --git a/libbeat/common/seccomp/policy_linux_amd64.go b/libbeat/common/seccomp/policy_linux_amd64.go new file mode 100644 index 00000000000..4dce9b57250 --- /dev/null +++ b/libbeat/common/seccomp/policy_linux_amd64.go @@ -0,0 +1,122 @@ +package seccomp + +import ( + "github.com/elastic/go-seccomp-bpf" +) + +func init() { + defaultPolicy = &seccomp.Policy{ + DefaultAction: seccomp.ActionErrno, + Syscalls: []seccomp.SyscallGroup{ + { + Action: seccomp.ActionAllow, + Names: []string{ + "accept", + "accept4", + "access", + "arch_prctl", + "bind", + "brk", + "clock_gettime", + "clone", + "close", + "connect", + "dup", + "dup2", + "epoll_create", + "epoll_create1", + "epoll_ctl", + "epoll_pwait", + "epoll_wait", + "exit", + "exit_group", + "fchdir", + "fchmod", + "fchown", + "fcntl", + "fdatasync", + "flock", + "fstat", + "fsync", + "ftruncate", + "futex", + "getcwd", + "getdents", + "getdents64", + "geteuid", + "getgid", + "getpeername", + "getpid", + "getppid", + "getrandom", + "getrusage", + "getsockname", + "getsockopt", + "gettid", + "gettimeofday", + "getuid", + "inotify_add_watch", + "inotify_init1", + "inotify_rm_watch", + "ioctl", + "kill", + "listen", + "lseek", + "lstat", + "madvise", + "mincore", + "mkdirat", + "mmap", + "mprotect", + "munmap", + "nanosleep", + "newfstatat", + "open", + "openat", + "pipe", + "pipe2", + "poll", + "pread64", + "pselect6", + "pwrite64", + "read", + "readlink", + "readlinkat", + "recvfrom", + "recvmmsg", + "recvmsg", + "rename", + "renameat", + "rt_sigaction", + "rt_sigprocmask", + "rt_sigreturn", + "sched_getaffinity", + "sched_yield", + "sendfile", + "sendmmsg", + "sendmsg", + "sendto", + "set_robust_list", + "setitimer", + "setsockopt", + "shutdown", + "sigaltstack", + "socket", + "stat", + "statfs", + "sysinfo", + "tgkill", + "time", + "tkill", + "uname", + "unlink", + "unlinkat", + "wait4", + "waitid", + "write", + "writev", + }, + }, + }, + } +} diff --git a/libbeat/common/seccomp/policy_linux_arm.go b/libbeat/common/seccomp/policy_linux_arm.go new file mode 100644 index 00000000000..27558580e3c --- /dev/null +++ b/libbeat/common/seccomp/policy_linux_arm.go @@ -0,0 +1,20 @@ +package seccomp + +import "github.com/elastic/go-seccomp-bpf" + +func init() { + defaultPolicy = &seccomp.Policy{ + DefaultAction: seccomp.ActionAllow, + Syscalls: []seccomp.SyscallGroup{ + { + Action: seccomp.ActionErrno, + Names: []string{ + "execve", + "execveat", + "fork", + "vfork", + }, + }, + }, + } +} diff --git a/libbeat/common/seccomp/seccomp-profiler-allow.txt b/libbeat/common/seccomp/seccomp-profiler-allow.txt new file mode 100644 index 00000000000..cb931276046 --- /dev/null +++ b/libbeat/common/seccomp/seccomp-profiler-allow.txt @@ -0,0 +1,26 @@ +# cgo pthread +mprotect +set_robust_list +tgkill +time + +# cgo os/user +access +open +stat + +# cgo tsg/gopacket +poll + +# system testing binaries w/ race detector +clock_gettime +pipe +pipe2 +getdents +getppid +gettimeofday +nanosleep +readlink +rename +unlink +wait4 diff --git a/libbeat/common/seccomp/seccomp-profiler-blacklist.txt b/libbeat/common/seccomp/seccomp-profiler-blacklist.txt new file mode 100644 index 00000000000..f8d2f0cc56c --- /dev/null +++ b/libbeat/common/seccomp/seccomp-profiler-blacklist.txt @@ -0,0 +1,31 @@ +chdir +chroot +clock_adjtime +create_module +delete_module +execve +execveat +fork +init_module +mount +prctl +ptrace +reboot +seccomp +setdomainname +setfsgid +setfsuid +setgid +setgroups +sethostname +setpgid +setregid +setresgid +setresuid +setreuid +setsid +settimeofday +setuid +unmount2 +unshare +vfork diff --git a/libbeat/common/seccomp/seccomp.go b/libbeat/common/seccomp/seccomp.go new file mode 100644 index 00000000000..cc6e9cd3e06 --- /dev/null +++ b/libbeat/common/seccomp/seccomp.go @@ -0,0 +1,113 @@ +package seccomp + +import ( + "runtime" + + "github.com/pkg/errors" + + "github.com/elastic/beats/libbeat/common" + "github.com/elastic/beats/libbeat/logp" + "github.com/elastic/go-seccomp-bpf" +) + +var ( + defaultPolicy *seccomp.Policy + registeredPolicy *seccomp.Policy +) + +// MustRegisterPolicy registers a seccomp policy to use instead of the default +// policy. This can be used to register an application specific seccomp policy +// that is tailored to the specific system calls that the application requires. +// It panics if a policy has already been registered or if the given policy +// is invalid. +func MustRegisterPolicy(p *seccomp.Policy) { + if p == nil { + panic(errors.New("seccomp policy cannot be nil")) + } + + if registeredPolicy != nil { + panic(errors.New("a seccomp policy is already registered")) + } + + // Ensure that the policy is valid and usable. + if _, err := p.Assemble(); err != nil { + panic(errors.Wrap(err, "failed to register seccomp policy")) + } + registeredPolicy = p +} + +// LoadFilter loads a seccomp system call filter into the kernel for this +// process. This feature is only available on Linux 3.17+. If c is nil or does +// not contain a seccomp policy then a default policy will be used. +// +// An error is returned if there is a config validation problem. Otherwise any +// errors interfacing with the kernel are logged (i.e. it is non-fatal if +// seccomp cannot be setup). +// +// Policy precedence order (highest to lowest): +// - Policy values from config +// - Application registered policy +// - Default policy (a simple blacklist) +func LoadFilter(c *common.Config) error { + // Bail out if seccomp.enabled=false. + if c != nil && !c.Enabled() { + return nil + } + + p, err := getPolicy(c) + if err != nil { + return err + } + + loadFilter(p) + return nil +} + +// loadFilter loads a system call filter. +func loadFilter(p *seccomp.Policy) { + log := logp.NewLogger("seccomp") + + if runtime.GOOS != "linux" { + log.Debug("Syscall filtering is only supported on Linux") + return + } + + if p == nil { + log.Debug("No seccomp policy is defined") + return + } + + filter := seccomp.Filter{ + NoNewPrivs: true, + Flag: seccomp.FilterFlagTSync, + Policy: *p, + } + + log.Debug("Loading syscall filter") + log = log.With("seccomp_filter", filter) + if err := seccomp.LoadFilter(filter); err != nil { + log.Warnw("Syscall filter could not be installed", "error", err) + return + } + + log.Infow("Syscall filter successfully installed") +} + +func getPolicy(c *common.Config) (*seccomp.Policy, error) { + policy := defaultPolicy + if registeredPolicy != nil { + policy = registeredPolicy + } + + if c != nil && (c.HasField("default_action") || c.HasField("syscalls")) { + if policy == nil { + policy = &seccomp.Policy{} + } + + if err := c.Unpack(policy); err != nil { + return nil, err + } + } + + return policy, nil +} diff --git a/libbeat/docs/security/linux-seccomp.asciidoc b/libbeat/docs/security/linux-seccomp.asciidoc index 68f186cf6cc..5ec897582b5 100644 --- a/libbeat/docs/security/linux-seccomp.asciidoc +++ b/libbeat/docs/security/linux-seccomp.asciidoc @@ -15,10 +15,21 @@ surface exposed to {beatname_uc} (principle of least privilege). This minimizes the impact of unknown vulnerabilities that might be found in the process. The filter is expressed as a Berkeley Packet Filter (BPF) program. The BPF -program is generated based on a policy defined in the {beatname_uc} -configuration. If you don't specify a policy, {beatname_uc} uses a minimal -default blacklist policy that prohibits `execve`, `execveat`, `fork`, and -`vfork` syscalls. This is the default policy. +program is generated based on a policy defined by {beatname_uc}. The policy +can be customized through configuration as well. + +A seccomp policy is architecture specific due to the fact that system calls vary +by architecture. {beatname_uc} includes a whitelist seccomp policy for the +amd64 and 386 architectures. You can view those policies +https://github.com/elastic/beats/tree/{doc-branch}/libbeat/common/seccomp[here]. + +[float] +[[seccomp-policy-config]] +=== Seccomp Policy Configuration + +The seccomp policy can be customized through the configuration policy. This is +an example blacklist policy that prohibits `execve`, `execveat`, `fork`, and +`vfork` syscalls. [source,yaml] ---- @@ -38,10 +49,6 @@ names below then it will be allowed. error will be returned to caller. This is known as a blacklist policy. <3> These are system calls being prohibited. -[float] -[[seccomp-policy-config]] -=== Seccomp Policy Configuration - These are the configuration options for a seccomp policy. *`enabled`*:: On Linux, this option is enabled by default. To disable seccomp @@ -68,7 +75,7 @@ actions that are available depend on the kernel version. - `trace` - The kernel will notify a `ptrace` tracer. If no tracer is present then the system call fails with `ENOSYS` (function not implemented). - `trap` - The kernel will send a `SIGSYS` signal to the calling thread and not - execute the system call. + execute the system call. The Go runtime will exit. - `kill_thread` - The kernel will immediately terminate the thread. Other threads will continue to execute. - `kill_process` - The kernel will terminate the process. Available in Linux diff --git a/libbeat/scripts/Makefile b/libbeat/scripts/Makefile index 5df97325955..ab911ad0af9 100755 --- a/libbeat/scripts/Makefile +++ b/libbeat/scripts/Makefile @@ -17,6 +17,9 @@ PACKER_TEMPLATES_DIR?=${ES_BEATS}/dev-tools/packer ## @Building Directory of tem NOTICE_FILE?=../NOTICE.txt LICENSE_FILE?=../licenses/APACHE-LICENSE-2.0.txt ELASTIC_LICENSE_FILE?=../licenses/ELASTIC-LICENSE.txt +SECCOMP_BINARY?=${BEAT_NAME} +SECCOMP_BLACKLIST?=${ES_BEATS}/libbeat/common/seccomp/seccomp-profiler-blacklist.txt +SECCOMP_ALLOWLIST?=${ES_BEATS}/libbeat/common/seccomp/seccomp-profiler-allow.txt space:=$() # comma:=, @@ -548,3 +551,23 @@ help: ## @help Show this help. help_variables: ## @help Show Makefile customizable variables. @python ${ES_BEATS}/libbeat/scripts/generate_makefile_doc.py --variables $(MAKEFILE_LIST) + +# Generates a seccomp whitelist policy for the binary pointed to by +# SECCOMP_BINARY. +.PHONY: seccomp +seccomp: + @go get github.com/elastic/beats/vendor/github.com/elastic/go-seccomp-bpf/cmd/seccomp-profiler + @test -f ${SECCOMP_BINARY} || (echo "${SECCOMP_BINARY} binary is not built."; false) + seccomp-profiler \ + -b "$(shell grep -v ^# "${SECCOMP_BLACKLIST}")" \ + -allow "$(shell grep -v ^# "${SECCOMP_ALLOWLIST}")" \ + -t "${ES_BEATS}/libbeat/common/seccomp/policy.go.tpl" \ + -pkg include \ + -out "include/seccomp_linux_{{.GOARCH}}.go" \ + ${SECCOMP_BINARY} + +# Generates seccomp profiles based on the binaries produced by the package target. +.PHONY: seccomp-package +seccomp-package: + SECCOMP_BINARY=build/package/${BEAT_NAME}-linux-386 $(MAKE) seccomp + SECCOMP_BINARY=build/package/${BEAT_NAME}-linux-amd64 $(MAKE) seccomp diff --git a/metricbeat/metricbeat.reference.yml b/metricbeat/metricbeat.reference.yml index 01995bfa5de..63b6a004ae3 100644 --- a/metricbeat/metricbeat.reference.yml +++ b/metricbeat/metricbeat.reference.yml @@ -1582,16 +1582,4 @@ logging.files: #============================= Process Security ================================ # Enable or disable seccomp system call filtering on Linux. Default is enabled. -seccomp.enabled: true - -# The default seccomp policy. This policy blacklists system calls that allow -# process creation. -seccomp: - default_action: allow - syscalls: - - action: errno - names: - - execve - - execveat - - fork - - vfork +#seccomp.enabled: true diff --git a/packetbeat/packetbeat.reference.yml b/packetbeat/packetbeat.reference.yml index 1a8c4bdf102..3365a05d77a 100644 --- a/packetbeat/packetbeat.reference.yml +++ b/packetbeat/packetbeat.reference.yml @@ -1460,16 +1460,4 @@ logging.files: #============================= Process Security ================================ # Enable or disable seccomp system call filtering on Linux. Default is enabled. -seccomp.enabled: true - -# The default seccomp policy. This policy blacklists system calls that allow -# process creation. -seccomp: - default_action: allow - syscalls: - - action: errno - names: - - execve - - execveat - - fork - - vfork +#seccomp.enabled: true diff --git a/winlogbeat/winlogbeat.reference.yml b/winlogbeat/winlogbeat.reference.yml index 7885d029e25..6683e18fe67 100644 --- a/winlogbeat/winlogbeat.reference.yml +++ b/winlogbeat/winlogbeat.reference.yml @@ -1012,16 +1012,4 @@ logging.files: #============================= Process Security ================================ # Enable or disable seccomp system call filtering on Linux. Default is enabled. -seccomp.enabled: true - -# The default seccomp policy. This policy blacklists system calls that allow -# process creation. -seccomp: - default_action: allow - syscalls: - - action: errno - names: - - execve - - execveat - - fork - - vfork +#seccomp.enabled: true