From bc80df8c19f4301a4b58bf862eec73f46344a621 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Mon, 4 Dec 2023 13:46:57 +0100 Subject: [PATCH 01/21] Capabilities normalization and auditbeat support This draft implements `process.thread.capabilities.{effective,permitted}` to auditbeat/system/process and normalizes the other uses of linux capabilities across beats (only two other cases). Introduces libbeat/common/capabilities that returns strings in the format ECS expects. It also condenses "all capabilities" into CAP_ALL as suggested. I've tested metricbeat, auditbeat and filebear+journald. --- .../input/journald/pkg/journalfield/conv.go | 65 +----- .../pkg/journalfield/conv_expand_test.go | 46 +--- .../common/capabilities/capabilities_linux.go | 208 ++++++++++++++++++ .../capabilities/capabilities_linux_test.go | 97 ++++++++ .../common/capabilities/capabilities_other.go | 50 +++++ libbeat/common/capabilities_linux.go | 66 ------ libbeat/common/seccomp/policy_linux_386.go | 1 + libbeat/common/seccomp/policy_linux_amd64.go | 1 + metricbeat/helper/socket/ptable_linux.go | 15 +- .../module/system/process/process.go | 31 ++- 10 files changed, 398 insertions(+), 182 deletions(-) create mode 100644 libbeat/common/capabilities/capabilities_linux.go create mode 100644 libbeat/common/capabilities/capabilities_linux_test.go create mode 100644 libbeat/common/capabilities/capabilities_other.go delete mode 100644 libbeat/common/capabilities_linux.go diff --git a/filebeat/input/journald/pkg/journalfield/conv.go b/filebeat/input/journald/pkg/journalfield/conv.go index bd7403ae142f..94447b773b7e 100644 --- a/filebeat/input/journald/pkg/journalfield/conv.go +++ b/filebeat/input/journald/pkg/journalfield/conv.go @@ -19,11 +19,11 @@ package journalfield import ( "fmt" - "math/bits" "regexp" "strconv" "strings" + "github.com/elastic/beats/v7/libbeat/common/capabilities" "github.com/elastic/elastic-agent-libs/logp" "github.com/elastic/elastic-agent-libs/mapstr" ) @@ -190,72 +190,13 @@ func expandCapabilities(fields mapstr.M) { if !ok { return } - w, err := strconv.ParseUint(c, 16, 64) - if err != nil { - return - } - if w == 0 { + caps, err := capabilities.FromString(c, 16) + if err != nil || len(caps) == 0 { return } - caps := make([]string, 0, bits.OnesCount64(w)) - for i := 0; w != 0; i++ { - if w&1 != 0 { - if i < len(capTable) { - caps = append(caps, capTable[i]) - } else { - caps = append(caps, strconv.Itoa(i)) - } - } - w >>= 1 - } fields.Put("process.thread.capabilities.effective", caps) } -// include/uapi/linux/capability.h -var capTable = [...]string{ - 0: "CAP_CHOWN", - 1: "CAP_DAC_OVERRIDE", - 2: "CAP_DAC_READ_SEARCH", - 3: "CAP_FOWNER", - 4: "CAP_FSETID", - 5: "CAP_KILL", - 6: "CAP_SETGID", - 7: "CAP_SETUID", - 8: "CAP_SETPCAP", - 9: "CAP_LINUX_IMMUTABLE", - 10: "CAP_NET_BIND_SERVICE", - 11: "CAP_NET_BROADCAST", - 12: "CAP_NET_ADMIN", - 13: "CAP_NET_RAW", - 14: "CAP_IPC_LOCK", - 15: "CAP_IPC_OWNER", - 16: "CAP_SYS_MODULE", - 17: "CAP_SYS_RAWIO", - 18: "CAP_SYS_CHROOT", - 19: "CAP_SYS_PTRACE", - 20: "CAP_SYS_PACCT", - 21: "CAP_SYS_ADMIN", - 22: "CAP_SYS_BOOT", - 23: "CAP_SYS_NICE", - 24: "CAP_SYS_RESOURCE", - 25: "CAP_SYS_TIME", - 26: "CAP_SYS_TTY_CONFIG", - 27: "CAP_MKNOD", - 28: "CAP_LEASE", - 29: "CAP_AUDIT_WRITE", - 30: "CAP_AUDIT_CONTROL", - 31: "CAP_SETFCAP", - 32: "CAP_MAC_OVERRIDE", - 33: "CAP_MAC_ADMIN", - 34: "CAP_SYSLOG", - 35: "CAP_WAKE_ALARM", - 36: "CAP_BLOCK_SUSPEND", - 37: "CAP_AUDIT_READ", - 38: "CAP_PERFMON", - 39: "CAP_BPF", - 40: "CAP_CHECKPOINT_RESTORE", -} - func getStringFromFields(key string, fields mapstr.M) string { value, _ := fields.GetValue(key) str, _ := value.(string) diff --git a/filebeat/input/journald/pkg/journalfield/conv_expand_test.go b/filebeat/input/journald/pkg/journalfield/conv_expand_test.go index c43e57a1c494..e1ae0aa6378a 100644 --- a/filebeat/input/journald/pkg/journalfield/conv_expand_test.go +++ b/filebeat/input/journald/pkg/journalfield/conv_expand_test.go @@ -121,47 +121,7 @@ var expandCapabilitiesTests = []struct { "thread": mapstr.M{ "capabilities": mapstr.M{ "effective": []string{ - "CAP_CHOWN", - "CAP_DAC_OVERRIDE", - "CAP_DAC_READ_SEARCH", - "CAP_FOWNER", - "CAP_FSETID", - "CAP_KILL", - "CAP_SETGID", - "CAP_SETUID", - "CAP_SETPCAP", - "CAP_LINUX_IMMUTABLE", - "CAP_NET_BIND_SERVICE", - "CAP_NET_BROADCAST", - "CAP_NET_ADMIN", - "CAP_NET_RAW", - "CAP_IPC_LOCK", - "CAP_IPC_OWNER", - "CAP_SYS_MODULE", - "CAP_SYS_RAWIO", - "CAP_SYS_CHROOT", - "CAP_SYS_PTRACE", - "CAP_SYS_PACCT", - "CAP_SYS_ADMIN", - "CAP_SYS_BOOT", - "CAP_SYS_NICE", - "CAP_SYS_RESOURCE", - "CAP_SYS_TIME", - "CAP_SYS_TTY_CONFIG", - "CAP_MKNOD", - "CAP_LEASE", - "CAP_AUDIT_WRITE", - "CAP_AUDIT_CONTROL", - "CAP_SETFCAP", - "CAP_MAC_OVERRIDE", - "CAP_MAC_ADMIN", - "CAP_SYSLOG", - "CAP_WAKE_ALARM", - "CAP_BLOCK_SUSPEND", - "CAP_AUDIT_READ", - "CAP_PERFMON", - "CAP_BPF", - "CAP_CHECKPOINT_RESTORE", + "CAP_ALL", }, }, }, @@ -228,8 +188,8 @@ var expandCapabilitiesTests = []struct { "CAP_PERFMON", "CAP_BPF", "CAP_CHECKPOINT_RESTORE", - "41", - "42", + "CAP_41", + "CAP_42", }, }, }, diff --git a/libbeat/common/capabilities/capabilities_linux.go b/libbeat/common/capabilities/capabilities_linux.go new file mode 100644 index 000000000000..68dc882491cb --- /dev/null +++ b/libbeat/common/capabilities/capabilities_linux.go @@ -0,0 +1,208 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//go:build linux + +package capabilities + +import ( + "errors" + "math/bits" + "strconv" + "strings" + + "kernel.org/pub/linux/libs/security/libcap/cap" +) + +var ( + // Tried to map an invalid capability ID: x < 0 || x >= 64. + errInvalidCapability = errors.New("Invalid capability") + + // Capabilities are linux only and this is returned on other + // systems for all the public functions. There is a generic + // errors.ErrUnsupported present in golang 1.21, but still + // support 1.20. + ErrUnsupported = errors.New("Capabilities are only supported in linux") +) + +// The capability set flag/vector, re-exported from +// libcap(3). Inherit, Bound & Ambient not exported since we have no +// use for it yet. +type Flag = cap.Flag + +const ( + // aka CapEff + Effective = cap.Effective + // aka CapPrm + Permitted = cap.Permitted +) + +// True if sets are equal for the given flag/vector, errors out in +// case any of the sets is malformed. +func isEqual(flag Flag, a *cap.Set, b *cap.Set) (bool, error) { + d, err := a.Cf(b) + if err != nil { + return false, err + } + + return !d.Has(flag), nil +} + +// Convert the capability ID to a string suitable to be used in +// ECS. +// If capabiliy ID X is unknown, but valid (0 <= X < 64), "CAP_X" +// will be returned instead. Fetches from an internal table built at +// startup. +var toECS = makeToECS() + +// Make toECS() which creates a map of every possible valid capability +// ID on startup. Returns errInvalidCapabilty for an invalid ID. +func makeToECS() func(int) (string, error) { + ecsNames := make(map[int]string) + + for i := 0; i < 64; i++ { + c := cap.Value(i) + if i < int(cap.MaxBits()) { + ecsNames[i] = strings.ToUpper(c.String()) + } else { + ecsNames[i] = strings.ToUpper("CAP_" + c.String()) + } + } + + return func(b int) (string, error) { + s, ok := ecsNames[b] + if !ok { + return "", errInvalidCapability + } + return s, nil + } +} + +// True if the set has all the capabilities set for the given +// flag/vector, see FromUint64 for a CAP_ALL explanation. +var isAll = makeIsAll() + +// Make isAll(), there is no direct way to get a full capability set, +// so we have to build one. Instead of building it for every call, +// build it once on startup and don't expose it. +func makeIsAll() func(Flag, *cap.Set) (bool, error) { + all := cap.NewSet() + for i := 0; i < int(cap.MaxBits()); i++ { + all.SetFlag(cap.Effective, true, cap.Value(i)) + all.SetFlag(cap.Permitted, true, cap.Value(i)) + all.SetFlag(cap.Inheritable, true, cap.Value(i)) + } + + return func(flag Flag, set *cap.Set) (bool, error) { + return isEqual(flag, set, all) + } +} + +// Like isAll(), but for the empty set, here for symmetry. +var isEmpty = makeIsEmpty() + +// Make isEmpty(), the corollary to makeIsFull. +func makeIsEmpty() func(Flag, *cap.Set) (bool, error) { + empty := cap.NewSet() + + return func(flag Flag, set *cap.Set) (bool, error) { + return isEqual(flag, set, empty) + } +} + +// Fetch the capabilities of pid for a given flag/vector and convert +// it to the representation used in ECS. cap.GetPID() fetches it with +// SYS_CAPGET. Check FromUint64 for a definition of []{"CAP_ALL"}. +// May return ErrUnsupported on "not linux". +func FromPid(flag Flag, pid int) ([]string, error) { + set, err := cap.GetPID(pid) + if err != nil { + return []string{}, err + } + empty, err := isEmpty(flag, set) + if err != nil || empty { + return []string{}, err + } + all, err := isAll(flag, set) + if err != nil { + return []string{}, err + } + if all { + return []string{"CAP_ALL"}, nil + } + + var sl []string + for i := 0; i < int(cap.MaxBits()); i++ { + c := cap.Value(i) + v, err := set.GetFlag(flag, c) + if err != nil { + return []string{}, err + } + if !v { + continue + } + s, err := toECS(i) + // impossible since MaxBits <= 64 + if err != nil { + return []string{}, err + } + sl = append(sl, s) + } + + return sl, err +} + +// Convert a uint64 to the capabilities representation used in ECS. If +// all bits are set, []{"CAP_ALL"} is returned. The definition of what +// CAP_ALL is depends on the host as libcap(3) will probe the maximum +// number of capabilities on startup via cap.MaxBits(). +// May return ErrUnsupported on "not linux". +func FromUint64(w uint64) ([]string, error) { + allMask := (uint64(1) << uint64(cap.MaxBits())) - 1 + + if w == allMask { + return []string{"CAP_ALL"}, nil + } + + sl := make([]string, 0, bits.OnesCount64(w)) + for i := 0; w != 0; i++ { + if w & 1 != 0 { + s, err := toECS(i) + // impossible since MaxBits <= 64 + if err != nil { + return []string{}, err + } + sl = append(sl, s) + } + w >>= 1 + } + + return sl, nil +} + +// Convert a string to the capabilities representation used in +// ECS. Example input: "1ffffffffff", 16. See FromUint64 for details +// about CAP_ALL. +// May return ErrUnsupported on "not linux". +func FromString(s string, base int) ([]string, error) { + w, err := strconv.ParseUint(s, 16, 64) + if err != nil { + return []string{}, err + } + + return FromUint64(w) +} diff --git a/libbeat/common/capabilities/capabilities_linux_test.go b/libbeat/common/capabilities/capabilities_linux_test.go new file mode 100644 index 000000000000..0f765ddad731 --- /dev/null +++ b/libbeat/common/capabilities/capabilities_linux_test.go @@ -0,0 +1,97 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package capabilities + +import ( + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "kernel.org/pub/linux/libs/security/libcap/cap" +) + +var allMask = (uint64(1) << uint64(cap.MaxBits())) - 1 + +func TestEmpty(t *testing.T) { + sl, err := FromString("0", 16) + assert.Nil(t, err) + assert.Equal(t, len(sl), 0) + + sl, err = FromUint64(0) + assert.Nil(t, err) + assert.Equal(t, len(sl), 0) + + // assumes non root has no capabilities + if os.Geteuid() != 0 { + empty := cap.NewSet() + self := cap.GetProc() + d, err := self.Cf(empty) + assert.Nil(t, err) + assert.False(t, d.Has(cap.Effective)) + assert.False(t, d.Has(cap.Permitted)) + assert.False(t, d.Has(cap.Inheritable)) + } +} + +func TestAll(t *testing.T) { + all, err := FromString(fmt.Sprintf("%x", allMask), 16) + assert.Nil(t, err) + assert.Equal(t, all[0], "CAP_ALL") + + all, err = FromUint64(allMask) + assert.Nil(t, err) + assert.Equal(t, len(all), 1) + assert.Equal(t, all[0], "CAP_ALL") +} + +func TestOverflow(t *testing.T) { + sl, err := FromUint64(^uint64(0)) + assert.Nil(t, err) + assert.Equal(t, len(sl), 64) + + assertHasCap(t, sl, "CAP_CHOWN") + assertHasCap(t, sl, "CAP_DAC_OVERRIDE") + assertHasCap(t, sl, "CAP_DAC_READ_SEARCH") + assertHasCap(t, sl, "CAP_FOWNER") + assertHasCap(t, sl, "CAP_FSETID") + assertHasCap(t, sl, "CAP_KILL") + assertHasCap(t, sl, "CAP_SETGID") + assertHasCap(t, sl, "CAP_SYS_MODULE") + assertHasCap(t, sl, "CAP_SYS_RAWIO") + assertHasCap(t, sl, "CAP_IPC_LOCK") + assertHasCap(t, sl, "CAP_MAC_OVERRIDE") + if cap.MaxBits() <= 62 { + assertHasCap(t, sl, "CAP_62") + } + if cap.MaxBits() <= 63 { + assertHasCap(t, sl, "CAP_63") + } +} + +func assertHasCap(t *testing.T, sl []string, s string) { + var found int + + for _, s2 := range(sl) { + if s2 == s { + found++ + } + } + + assert.Equal(t, found, 1) +} diff --git a/libbeat/common/capabilities/capabilities_other.go b/libbeat/common/capabilities/capabilities_other.go new file mode 100644 index 000000000000..110af67bba10 --- /dev/null +++ b/libbeat/common/capabilities/capabilities_other.go @@ -0,0 +1,50 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//go:build !linux + +package capabilities + +import "errors" + +// Returned for all public functions on "not linux". +var ErrUnsupported = errors.New("Capabilities are only supported in linux") + +// Dummy value on "not linux". +type Flag = uint + +const ( + // Meaningless on "not linux". + Effective = Flag(0) + // Meaningless on "not linux". + Permitted = Flag(1) +) + +// ErrUnsupported on "not linux". +func FromPid(flag Flag, pid int) ([]string, error) { + return []string{}, ErrUnsupported +} + +// ErrUnsupported on "not linux". +func FromUint64(w uint64) ([]string, error) { + return []string{}, ErrUnsupported +} + +// ErrUnsupported on "not linux". +func FromString(s string, base int) ([]string, error) { + return []string{}, ErrUnsupported +} diff --git a/libbeat/common/capabilities_linux.go b/libbeat/common/capabilities_linux.go deleted file mode 100644 index b2992c251ef8..000000000000 --- a/libbeat/common/capabilities_linux.go +++ /dev/null @@ -1,66 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -//go:build linux - -package common - -import ( - "errors" - "fmt" - - "github.com/elastic/go-sysinfo" - "github.com/elastic/go-sysinfo/types" -) - -// Capabilities contains the capability sets of a process -type Capabilities types.CapabilityInfo - -// Check performs a permission check for a given capabilities set -func (c Capabilities) Check(set []string) bool { - for _, capability := range set { - found := false - for _, effective := range c.Effective { - if capability == effective { - found = true - break - } - } - if !found { - return false - } - } - return true -} - -// GetCapabilities gets the capabilities of this process -func GetCapabilities() (Capabilities, error) { - p, err := sysinfo.Self() - if err != nil { - return Capabilities{}, fmt.Errorf("failed to read self process information: %w", err) - } - - if c, ok := p.(types.Capabilities); ok { - capabilities, err := c.Capabilities() - if err != nil { - return Capabilities{}, fmt.Errorf("failed to read process capabilities: %w", err) - } - return Capabilities(*capabilities), nil - } - - return Capabilities{}, errors.New("capabilities not available") -} diff --git a/libbeat/common/seccomp/policy_linux_386.go b/libbeat/common/seccomp/policy_linux_386.go index 724666987201..ac2a93a5c741 100644 --- a/libbeat/common/seccomp/policy_linux_386.go +++ b/libbeat/common/seccomp/policy_linux_386.go @@ -31,6 +31,7 @@ func init() { "_llseek", "access", "brk", + "capget", "chmod", "chown", "clock_gettime", diff --git a/libbeat/common/seccomp/policy_linux_amd64.go b/libbeat/common/seccomp/policy_linux_amd64.go index 0a05bdde9275..624f48c890a2 100644 --- a/libbeat/common/seccomp/policy_linux_amd64.go +++ b/libbeat/common/seccomp/policy_linux_amd64.go @@ -34,6 +34,7 @@ func init() { "arch_prctl", "bind", "brk", + "capget", "chmod", "chown", "clock_gettime", diff --git a/metricbeat/helper/socket/ptable_linux.go b/metricbeat/helper/socket/ptable_linux.go index 88fff488bc22..ffe585f70949 100644 --- a/metricbeat/helper/socket/ptable_linux.go +++ b/metricbeat/helper/socket/ptable_linux.go @@ -20,17 +20,22 @@ package socket import ( - "github.com/elastic/beats/v7/libbeat/common" + "kernel.org/pub/linux/libs/security/libcap/cap" ) -var requiredCapabilities = []string{"sys_ptrace", "dac_read_search"} - // isPrivileged checks if this process has privileges to read sockets // of all users func isPrivileged() (bool, error) { - capabilities, err := common.GetCapabilities() + set := cap.GetProc() + + ptrace, err := set.GetFlag(cap.Effective, cap.SYS_PTRACE) + if err != nil { + return false, err + } + dac_read_search, err := set.GetFlag(cap.Effective, cap.DAC_READ_SEARCH) if err != nil { return false, err } - return capabilities.Check(requiredCapabilities), nil + + return ptrace && dac_read_search, nil } diff --git a/x-pack/auditbeat/module/system/process/process.go b/x-pack/auditbeat/module/system/process/process.go index d2dfae065980..b739b45a2c2b 100644 --- a/x-pack/auditbeat/module/system/process/process.go +++ b/x-pack/auditbeat/module/system/process/process.go @@ -6,6 +6,7 @@ package process import ( "encoding/binary" + "errors" "fmt" "os" "os/user" @@ -18,6 +19,7 @@ import ( "github.com/elastic/beats/v7/auditbeat/datastore" "github.com/elastic/beats/v7/auditbeat/helper/hasher" + "github.com/elastic/beats/v7/libbeat/common/capabilities" "github.com/elastic/beats/v7/libbeat/common/cfgwarn" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/x-pack/auditbeat/cache" @@ -101,12 +103,14 @@ type MetricSet struct { // Process represents information about a process. type Process struct { - Info types.ProcessInfo - UserInfo *types.UserInfo - User *user.User - Group *user.Group - Hashes map[hasher.HashType]hasher.Digest - Error error + Info types.ProcessInfo + UserInfo *types.UserInfo + User *user.User + Group *user.Group + CapEffective []string + CapPermitted []string + Hashes map[hasher.HashType]hasher.Digest + Error error } // Hash creates a hash for Process. @@ -376,6 +380,13 @@ func (ms *MetricSet) processEvent(process *Process, eventType string, action eve event.RootFields.Put("user.group.name", process.Group.Name) } + if len(process.CapEffective) > 0 { + event.RootFields.Put("process.thread.capabilities.effective", process.CapEffective) + } + if len(process.CapPermitted) > 0 { + event.RootFields.Put("process.thread.capabilities.permitted", process.CapPermitted) + } + if process.Hashes != nil { for hashType, digest := range process.Hashes { fieldName := "process.hash." + string(hashType) @@ -488,6 +499,14 @@ func (ms *MetricSet) getProcesses() ([]*Process, error) { process.UserInfo = &userInfo } + process.CapEffective, err = capabilities.FromPid(capabilities.Effective, pInfo.PID) + if err != nil && !errors.Is(err, capabilities.ErrUnsupported) && process.Error == nil { + process.Error = err + } + process.CapPermitted, err = capabilities.FromPid(capabilities.Permitted, pInfo.PID) + if err != nil && !errors.Is(err, capabilities.ErrUnsupported) && process.Error == nil { + process.Error = err + } // Exclude Linux kernel processes, they are not very interesting. if runtime.GOOS == "linux" && userInfo.UID == "0" && process.Info.Exe == "" { continue From 593afcc31efb9ee88c16503e202fad41a315f667 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Wed, 13 Dec 2023 17:48:39 +0100 Subject: [PATCH 02/21] Error messages should be lowercase, thanks matt@ --- libbeat/common/capabilities/capabilities_linux.go | 4 ++-- libbeat/common/capabilities/capabilities_other.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libbeat/common/capabilities/capabilities_linux.go b/libbeat/common/capabilities/capabilities_linux.go index 68dc882491cb..e36fe4a43e7b 100644 --- a/libbeat/common/capabilities/capabilities_linux.go +++ b/libbeat/common/capabilities/capabilities_linux.go @@ -30,13 +30,13 @@ import ( var ( // Tried to map an invalid capability ID: x < 0 || x >= 64. - errInvalidCapability = errors.New("Invalid capability") + errInvalidCapability = errors.New("invalid capability") // Capabilities are linux only and this is returned on other // systems for all the public functions. There is a generic // errors.ErrUnsupported present in golang 1.21, but still // support 1.20. - ErrUnsupported = errors.New("Capabilities are only supported in linux") + ErrUnsupported = errors.New("capabilities are only supported in linux") ) // The capability set flag/vector, re-exported from diff --git a/libbeat/common/capabilities/capabilities_other.go b/libbeat/common/capabilities/capabilities_other.go index 110af67bba10..5f13e00df1b6 100644 --- a/libbeat/common/capabilities/capabilities_other.go +++ b/libbeat/common/capabilities/capabilities_other.go @@ -22,7 +22,7 @@ package capabilities import "errors" // Returned for all public functions on "not linux". -var ErrUnsupported = errors.New("Capabilities are only supported in linux") +var ErrUnsupported = errors.New("capabilities are only supported in linux") // Dummy value on "not linux". type Flag = uint From 810f4c2caf62ed906ffa2f0e98aabff1f7789e84 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Wed, 13 Dec 2023 18:04:39 +0100 Subject: [PATCH 03/21] Turn allMask into a package var, thanks to matt --- libbeat/common/capabilities/capabilities_linux.go | 5 +++-- libbeat/common/capabilities/capabilities_linux_test.go | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libbeat/common/capabilities/capabilities_linux.go b/libbeat/common/capabilities/capabilities_linux.go index e36fe4a43e7b..bd6727311fb6 100644 --- a/libbeat/common/capabilities/capabilities_linux.go +++ b/libbeat/common/capabilities/capabilities_linux.go @@ -37,6 +37,9 @@ var ( // errors.ErrUnsupported present in golang 1.21, but still // support 1.20. ErrUnsupported = errors.New("capabilities are only supported in linux") + + // The mask when all known capabilities are set. + allMask = (uint64(1) << uint64(cap.MaxBits())) - 1 ) // The capability set flag/vector, re-exported from @@ -172,8 +175,6 @@ func FromPid(flag Flag, pid int) ([]string, error) { // number of capabilities on startup via cap.MaxBits(). // May return ErrUnsupported on "not linux". func FromUint64(w uint64) ([]string, error) { - allMask := (uint64(1) << uint64(cap.MaxBits())) - 1 - if w == allMask { return []string{"CAP_ALL"}, nil } diff --git a/libbeat/common/capabilities/capabilities_linux_test.go b/libbeat/common/capabilities/capabilities_linux_test.go index 0f765ddad731..fb85e33c7146 100644 --- a/libbeat/common/capabilities/capabilities_linux_test.go +++ b/libbeat/common/capabilities/capabilities_linux_test.go @@ -26,8 +26,6 @@ import ( "kernel.org/pub/linux/libs/security/libcap/cap" ) -var allMask = (uint64(1) << uint64(cap.MaxBits())) - 1 - func TestEmpty(t *testing.T) { sl, err := FromString("0", 16) assert.Nil(t, err) From cac206b26b552abb814370335e29bfe6d392581c Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Wed, 13 Dec 2023 18:11:20 +0100 Subject: [PATCH 04/21] range()->range, thanks to matt --- libbeat/common/capabilities/capabilities_linux_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libbeat/common/capabilities/capabilities_linux_test.go b/libbeat/common/capabilities/capabilities_linux_test.go index fb85e33c7146..419260685f89 100644 --- a/libbeat/common/capabilities/capabilities_linux_test.go +++ b/libbeat/common/capabilities/capabilities_linux_test.go @@ -85,7 +85,7 @@ func TestOverflow(t *testing.T) { func assertHasCap(t *testing.T, sl []string, s string) { var found int - for _, s2 := range(sl) { + for _, s2 := range sl { if s2 == s { found++ } From a3e7b8b1af147b275f78592a2934525949007887 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Mon, 18 Dec 2023 10:11:48 +0100 Subject: [PATCH 05/21] typo --- libbeat/common/capabilities/capabilities_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libbeat/common/capabilities/capabilities_linux.go b/libbeat/common/capabilities/capabilities_linux.go index bd6727311fb6..e087cb18ae4e 100644 --- a/libbeat/common/capabilities/capabilities_linux.go +++ b/libbeat/common/capabilities/capabilities_linux.go @@ -34,7 +34,7 @@ var ( // Capabilities are linux only and this is returned on other // systems for all the public functions. There is a generic - // errors.ErrUnsupported present in golang 1.21, but still + // errors.ErrUnsupported present in golang 1.21, but we still // support 1.20. ErrUnsupported = errors.New("capabilities are only supported in linux") From b6859ab56d5707711a4bc138e48d33fa86df2a5a Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Mon, 18 Dec 2023 10:38:17 +0100 Subject: [PATCH 06/21] Add process.thread.capabilities to ECS fields These fields are only present in ECS 8.10, it seems beats is still tracking ECS 8.0, is this the right way to do until we track 8.10+? It's enough for `mage integTest` to pass, otherwise it will complain about undocumented new fields --- auditbeat/docs/fields.asciidoc | 22 +++++++++++++++++++ .../auditbeat/module/system/_meta/fields.yml | 19 ++++++++++++++++ x-pack/auditbeat/module/system/fields.go | 2 +- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/auditbeat/docs/fields.asciidoc b/auditbeat/docs/fields.asciidoc index bd4db4ce5b6c..9eee5f008fc1 100644 --- a/auditbeat/docs/fields.asciidoc +++ b/auditbeat/docs/fields.asciidoc @@ -18925,6 +18925,28 @@ type: keyword -- +*`process.thread.capabilities.effective`*:: ++ +-- +This is the set of capabilities used by the kernel to perform permission checks for the thread. + +type: keyword + +example: ["CAP_BPF", "CAP_SYS_ADMIN"] + +-- + +*`process.thread.capabilities.permitted`*:: ++ +-- +This is a limiting superset for the effective capabilities that the thread may assume. + +type: keyword + +example: ["CAP_BPF", "CAP_SYS_ADMIN"] + +-- + [float] === hash diff --git a/x-pack/auditbeat/module/system/_meta/fields.yml b/x-pack/auditbeat/module/system/_meta/fields.yml index 61908a6ce292..43101839c0a1 100644 --- a/x-pack/auditbeat/module/system/_meta/fields.yml +++ b/x-pack/auditbeat/module/system/_meta/fields.yml @@ -30,6 +30,25 @@ - name: process type: group fields: + - name: thread.capabilities.effective + level: extended + type: keyword + ignore_above: 1024 + description: This is the set of capabilities used by the kernel to perform permission + checks for the thread. + example: '["CAP_BPF", "CAP_SYS_ADMIN"]' + pattern: ^(CAP_[A-Z_]+|\d+)$ + default_field: false + - name: thread.capabilities.permitted + level: extended + type: keyword + ignore_above: 1024 + description: This is a limiting superset for the effective capabilities that + the thread may assume. + example: '["CAP_BPF", "CAP_SYS_ADMIN"]' + pattern: ^(CAP_[A-Z_]+|\d+)$ + default_field: false + - name: hash type: group description: > diff --git a/x-pack/auditbeat/module/system/fields.go b/x-pack/auditbeat/module/system/fields.go index 7711dffe2c09..4b0a95d23b0a 100644 --- a/x-pack/auditbeat/module/system/fields.go +++ b/x-pack/auditbeat/module/system/fields.go @@ -19,5 +19,5 @@ func init() { // AssetSystem returns asset data. // This is the base64 encoded zlib format compressed contents of module/system. func AssetSystem() string { - return "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" + return "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" } From 1456e534d7b37b80ee548fe575aaeecec09f46c2 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Mon, 18 Dec 2023 13:39:55 +0100 Subject: [PATCH 07/21] Make gofmt happy --- .../common/capabilities/capabilities_linux.go | 2 +- .../auditbeat/module/system/process/process.go | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libbeat/common/capabilities/capabilities_linux.go b/libbeat/common/capabilities/capabilities_linux.go index e087cb18ae4e..fc1f8e5f9985 100644 --- a/libbeat/common/capabilities/capabilities_linux.go +++ b/libbeat/common/capabilities/capabilities_linux.go @@ -181,7 +181,7 @@ func FromUint64(w uint64) ([]string, error) { sl := make([]string, 0, bits.OnesCount64(w)) for i := 0; w != 0; i++ { - if w & 1 != 0 { + if w&1 != 0 { s, err := toECS(i) // impossible since MaxBits <= 64 if err != nil { diff --git a/x-pack/auditbeat/module/system/process/process.go b/x-pack/auditbeat/module/system/process/process.go index b739b45a2c2b..c389476615ba 100644 --- a/x-pack/auditbeat/module/system/process/process.go +++ b/x-pack/auditbeat/module/system/process/process.go @@ -103,14 +103,14 @@ type MetricSet struct { // Process represents information about a process. type Process struct { - Info types.ProcessInfo - UserInfo *types.UserInfo - User *user.User - Group *user.Group - CapEffective []string - CapPermitted []string - Hashes map[hasher.HashType]hasher.Digest - Error error + Info types.ProcessInfo + UserInfo *types.UserInfo + User *user.User + Group *user.Group + CapEffective []string + CapPermitted []string + Hashes map[hasher.HashType]hasher.Digest + Error error } // Hash creates a hash for Process. From 17d94faf93dc986da7ffe0c047ec7d4abbf9536c Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Mon, 18 Dec 2023 14:28:03 +0100 Subject: [PATCH 08/21] Curb journald test to linux --- filebeat/input/journald/pkg/journalfield/conv_expand_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/filebeat/input/journald/pkg/journalfield/conv_expand_test.go b/filebeat/input/journald/pkg/journalfield/conv_expand_test.go index e1ae0aa6378a..a3ff8867f63e 100644 --- a/filebeat/input/journald/pkg/journalfield/conv_expand_test.go +++ b/filebeat/input/journald/pkg/journalfield/conv_expand_test.go @@ -15,6 +15,8 @@ // specific language governing permissions and limitations // under the License. +//go:build linux && cgo + package journalfield import ( From 62ade139d9af7c9292fc3441c99f9bce2b11af89 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Wed, 20 Dec 2023 09:04:33 +0100 Subject: [PATCH 09/21] Update libbeat/common/capabilities/capabilities_linux_test.go Co-authored-by: Dan Kortschak <90160302+efd6@users.noreply.github.com> --- .../capabilities/capabilities_linux_test.go | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/libbeat/common/capabilities/capabilities_linux_test.go b/libbeat/common/capabilities/capabilities_linux_test.go index 419260685f89..6956e0c61b5e 100644 --- a/libbeat/common/capabilities/capabilities_linux_test.go +++ b/libbeat/common/capabilities/capabilities_linux_test.go @@ -63,17 +63,21 @@ func TestOverflow(t *testing.T) { assert.Nil(t, err) assert.Equal(t, len(sl), 64) - assertHasCap(t, sl, "CAP_CHOWN") - assertHasCap(t, sl, "CAP_DAC_OVERRIDE") - assertHasCap(t, sl, "CAP_DAC_READ_SEARCH") - assertHasCap(t, sl, "CAP_FOWNER") - assertHasCap(t, sl, "CAP_FSETID") - assertHasCap(t, sl, "CAP_KILL") - assertHasCap(t, sl, "CAP_SETGID") - assertHasCap(t, sl, "CAP_SYS_MODULE") - assertHasCap(t, sl, "CAP_SYS_RAWIO") - assertHasCap(t, sl, "CAP_IPC_LOCK") - assertHasCap(t, sl, "CAP_MAC_OVERRIDE") + for _, cap := range []string{ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_DAC_READ_SEARCH", + "CAP_FOWNER", + "CAP_FSETID", + "CAP_KILL", + "CAP_SETGID", + "CAP_SYS_MODULE", + "CAP_SYS_RAWIO", + "CAP_IPC_LOCK", + "CAP_MAC_OVERRIDE", + } { + assertHasCap(t, sl, cap) + } if cap.MaxBits() <= 62 { assertHasCap(t, sl, "CAP_62") } From 4e33d517d7ecb7f4a8d7b2d550f93c6c790ea133 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Wed, 20 Dec 2023 09:06:01 +0100 Subject: [PATCH 10/21] Update libbeat/common/capabilities/capabilities_linux_test.go Co-authored-by: Dan Kortschak <90160302+efd6@users.noreply.github.com> --- libbeat/common/capabilities/capabilities_linux_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libbeat/common/capabilities/capabilities_linux_test.go b/libbeat/common/capabilities/capabilities_linux_test.go index 6956e0c61b5e..70e9bc9e0e43 100644 --- a/libbeat/common/capabilities/capabilities_linux_test.go +++ b/libbeat/common/capabilities/capabilities_linux_test.go @@ -95,5 +95,5 @@ func assertHasCap(t *testing.T, sl []string, s string) { } } - assert.Equal(t, found, 1) + assert.Equal(t, found, 1, s) } From d7a0459f22ddd1a4daf346b1636ca43b9997d44c Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Wed, 20 Dec 2023 09:06:19 +0100 Subject: [PATCH 11/21] Update libbeat/common/capabilities/capabilities_other.go Co-authored-by: Dan Kortschak <90160302+efd6@users.noreply.github.com> --- libbeat/common/capabilities/capabilities_other.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libbeat/common/capabilities/capabilities_other.go b/libbeat/common/capabilities/capabilities_other.go index 5f13e00df1b6..c48aaf5c7471 100644 --- a/libbeat/common/capabilities/capabilities_other.go +++ b/libbeat/common/capabilities/capabilities_other.go @@ -36,7 +36,7 @@ const ( // ErrUnsupported on "not linux". func FromPid(flag Flag, pid int) ([]string, error) { - return []string{}, ErrUnsupported + return nil, ErrUnsupported } // ErrUnsupported on "not linux". From 51ce44efa9e15cab74710d32bef42e15b0bfee8d Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Tue, 19 Dec 2023 11:22:16 +0100 Subject: [PATCH 12/21] CHANGELOG.next.asciidoc entry --- CHANGELOG.next.asciidoc | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index ae767239de47..4b3cce5bfe3f 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -154,6 +154,7 @@ Setting environmental variable ELASTIC_NETINFO:false in Elastic Agent pod will d - Add `ignore_errors` option to audit module. {issue}15768[15768] {pull}36851[36851] - Fix copy arguments for strict aligned architectures. {pull}36976[36976] +- Add linux capabilities to processes in the system/process. {pull}37453[37453] *Filebeat* From c60b637db274e83795d526b07d0334ab7c2dac18 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Wed, 20 Dec 2023 09:20:29 +0100 Subject: [PATCH 13/21] Fix the remaining empty slices cases Thanks to @Tacklebox and @efd6 for the suggestion --- .../common/capabilities/capabilities_linux.go | 19 +++++++++++-------- .../common/capabilities/capabilities_other.go | 4 ++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/libbeat/common/capabilities/capabilities_linux.go b/libbeat/common/capabilities/capabilities_linux.go index fc1f8e5f9985..380c53b3ce50 100644 --- a/libbeat/common/capabilities/capabilities_linux.go +++ b/libbeat/common/capabilities/capabilities_linux.go @@ -134,15 +134,18 @@ func makeIsEmpty() func(Flag, *cap.Set) (bool, error) { func FromPid(flag Flag, pid int) ([]string, error) { set, err := cap.GetPID(pid) if err != nil { - return []string{}, err + return nil, err } empty, err := isEmpty(flag, set) - if err != nil || empty { - return []string{}, err + if err != nil { + return nil, err + } + if empty { + return []string{}, nil } all, err := isAll(flag, set) if err != nil { - return []string{}, err + return nil, err } if all { return []string{"CAP_ALL"}, nil @@ -153,7 +156,7 @@ func FromPid(flag Flag, pid int) ([]string, error) { c := cap.Value(i) v, err := set.GetFlag(flag, c) if err != nil { - return []string{}, err + return nil, err } if !v { continue @@ -161,7 +164,7 @@ func FromPid(flag Flag, pid int) ([]string, error) { s, err := toECS(i) // impossible since MaxBits <= 64 if err != nil { - return []string{}, err + return nil, err } sl = append(sl, s) } @@ -185,7 +188,7 @@ func FromUint64(w uint64) ([]string, error) { s, err := toECS(i) // impossible since MaxBits <= 64 if err != nil { - return []string{}, err + return nil, err } sl = append(sl, s) } @@ -202,7 +205,7 @@ func FromUint64(w uint64) ([]string, error) { func FromString(s string, base int) ([]string, error) { w, err := strconv.ParseUint(s, 16, 64) if err != nil { - return []string{}, err + return nil, err } return FromUint64(w) diff --git a/libbeat/common/capabilities/capabilities_other.go b/libbeat/common/capabilities/capabilities_other.go index c48aaf5c7471..05ab64e01eb7 100644 --- a/libbeat/common/capabilities/capabilities_other.go +++ b/libbeat/common/capabilities/capabilities_other.go @@ -41,10 +41,10 @@ func FromPid(flag Flag, pid int) ([]string, error) { // ErrUnsupported on "not linux". func FromUint64(w uint64) ([]string, error) { - return []string{}, ErrUnsupported + return nil, ErrUnsupported } // ErrUnsupported on "not linux". func FromString(s string, base int) ([]string, error) { - return []string{}, ErrUnsupported + return nil, ErrUnsupported } From 020e2c5737167c1747327548d8bec60d8c7e6c62 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Wed, 20 Dec 2023 10:03:37 +0100 Subject: [PATCH 14/21] Shuffle things around like Dan suggested In go it's more common to start with the callers/public functions --- .../common/capabilities/capabilities_linux.go | 146 +++++++++--------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/libbeat/common/capabilities/capabilities_linux.go b/libbeat/common/capabilities/capabilities_linux.go index 380c53b3ce50..e8961e999ee8 100644 --- a/libbeat/common/capabilities/capabilities_linux.go +++ b/libbeat/common/capabilities/capabilities_linux.go @@ -54,79 +54,6 @@ const ( Permitted = cap.Permitted ) -// True if sets are equal for the given flag/vector, errors out in -// case any of the sets is malformed. -func isEqual(flag Flag, a *cap.Set, b *cap.Set) (bool, error) { - d, err := a.Cf(b) - if err != nil { - return false, err - } - - return !d.Has(flag), nil -} - -// Convert the capability ID to a string suitable to be used in -// ECS. -// If capabiliy ID X is unknown, but valid (0 <= X < 64), "CAP_X" -// will be returned instead. Fetches from an internal table built at -// startup. -var toECS = makeToECS() - -// Make toECS() which creates a map of every possible valid capability -// ID on startup. Returns errInvalidCapabilty for an invalid ID. -func makeToECS() func(int) (string, error) { - ecsNames := make(map[int]string) - - for i := 0; i < 64; i++ { - c := cap.Value(i) - if i < int(cap.MaxBits()) { - ecsNames[i] = strings.ToUpper(c.String()) - } else { - ecsNames[i] = strings.ToUpper("CAP_" + c.String()) - } - } - - return func(b int) (string, error) { - s, ok := ecsNames[b] - if !ok { - return "", errInvalidCapability - } - return s, nil - } -} - -// True if the set has all the capabilities set for the given -// flag/vector, see FromUint64 for a CAP_ALL explanation. -var isAll = makeIsAll() - -// Make isAll(), there is no direct way to get a full capability set, -// so we have to build one. Instead of building it for every call, -// build it once on startup and don't expose it. -func makeIsAll() func(Flag, *cap.Set) (bool, error) { - all := cap.NewSet() - for i := 0; i < int(cap.MaxBits()); i++ { - all.SetFlag(cap.Effective, true, cap.Value(i)) - all.SetFlag(cap.Permitted, true, cap.Value(i)) - all.SetFlag(cap.Inheritable, true, cap.Value(i)) - } - - return func(flag Flag, set *cap.Set) (bool, error) { - return isEqual(flag, set, all) - } -} - -// Like isAll(), but for the empty set, here for symmetry. -var isEmpty = makeIsEmpty() - -// Make isEmpty(), the corollary to makeIsFull. -func makeIsEmpty() func(Flag, *cap.Set) (bool, error) { - empty := cap.NewSet() - - return func(flag Flag, set *cap.Set) (bool, error) { - return isEqual(flag, set, empty) - } -} - // Fetch the capabilities of pid for a given flag/vector and convert // it to the representation used in ECS. cap.GetPID() fetches it with // SYS_CAPGET. Check FromUint64 for a definition of []{"CAP_ALL"}. @@ -210,3 +137,76 @@ func FromString(s string, base int) ([]string, error) { return FromUint64(w) } + +// True if sets are equal for the given flag/vector, errors out in +// case any of the sets is malformed. +func isEqual(flag Flag, a *cap.Set, b *cap.Set) (bool, error) { + d, err := a.Cf(b) + if err != nil { + return false, err + } + + return !d.Has(flag), nil +} + +// Convert the capability ID to a string suitable to be used in +// ECS. +// If capabiliy ID X is unknown, but valid (0 <= X < 64), "CAP_X" +// will be returned instead. Fetches from an internal table built at +// startup. +var toECS = makeToECS() + +// Make toECS() which creates a map of every possible valid capability +// ID on startup. Returns errInvalidCapabilty for an invalid ID. +func makeToECS() func(int) (string, error) { + ecsNames := make(map[int]string) + + for i := 0; i < 64; i++ { + c := cap.Value(i) + if i < int(cap.MaxBits()) { + ecsNames[i] = strings.ToUpper(c.String()) + } else { + ecsNames[i] = strings.ToUpper("CAP_" + c.String()) + } + } + + return func(b int) (string, error) { + s, ok := ecsNames[b] + if !ok { + return "", errInvalidCapability + } + return s, nil + } +} + +// True if the set has all the capabilities set for the given +// flag/vector, see FromUint64 for a CAP_ALL explanation. +var isAll = makeIsAll() + +// Make isAll(), there is no direct way to get a full capability set, +// so we have to build one. Instead of building it for every call, +// build it once on startup and don't expose it. +func makeIsAll() func(Flag, *cap.Set) (bool, error) { + all := cap.NewSet() + for i := 0; i < int(cap.MaxBits()); i++ { + all.SetFlag(cap.Effective, true, cap.Value(i)) + all.SetFlag(cap.Permitted, true, cap.Value(i)) + all.SetFlag(cap.Inheritable, true, cap.Value(i)) + } + + return func(flag Flag, set *cap.Set) (bool, error) { + return isEqual(flag, set, all) + } +} + +// Like isAll(), but for the empty set, here for symmetry. +var isEmpty = makeIsEmpty() + +// Make isEmpty(), the corollary to makeIsFull. +func makeIsEmpty() func(Flag, *cap.Set) (bool, error) { + empty := cap.NewSet() + + return func(flag Flag, set *cap.Set) (bool, error) { + return isEqual(flag, set, empty) + } +} From 28c241d9d61d34dd442ead95628b6d8223765e3f Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Tue, 9 Jan 2024 12:51:48 +0100 Subject: [PATCH 15/21] Rename 'v' to 'enabled' as suggested by Lee --- libbeat/common/capabilities/capabilities_linux.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libbeat/common/capabilities/capabilities_linux.go b/libbeat/common/capabilities/capabilities_linux.go index e8961e999ee8..9cd6737f8cc9 100644 --- a/libbeat/common/capabilities/capabilities_linux.go +++ b/libbeat/common/capabilities/capabilities_linux.go @@ -81,11 +81,11 @@ func FromPid(flag Flag, pid int) ([]string, error) { var sl []string for i := 0; i < int(cap.MaxBits()); i++ { c := cap.Value(i) - v, err := set.GetFlag(flag, c) + enabled, err := set.GetFlag(flag, c) if err != nil { return nil, err } - if !v { + if !enabled { continue } s, err := toECS(i) From 77218a7715048b9a21eb2be402a780599dd8a2ae Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 11 Jan 2024 14:52:38 +0100 Subject: [PATCH 16/21] Make the linter happy about error documentation --- libbeat/common/capabilities/capabilities_linux.go | 9 ++++----- libbeat/common/capabilities/capabilities_other.go | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/libbeat/common/capabilities/capabilities_linux.go b/libbeat/common/capabilities/capabilities_linux.go index 9cd6737f8cc9..c9c2951bf93d 100644 --- a/libbeat/common/capabilities/capabilities_linux.go +++ b/libbeat/common/capabilities/capabilities_linux.go @@ -29,13 +29,12 @@ import ( ) var ( - // Tried to map an invalid capability ID: x < 0 || x >= 64. + // errInvalidCapability expresses an invalid capability ID: x < 0 || x >= 64. errInvalidCapability = errors.New("invalid capability") - // Capabilities are linux only and this is returned on other - // systems for all the public functions. There is a generic - // errors.ErrUnsupported present in golang 1.21, but we still - // support 1.20. + // ErrUnsupported is returned for all public functions on "not + // linux". There is a generic errors.ErrUnsupported present in + // golang 1.21, but we still support 1.20. ErrUnsupported = errors.New("capabilities are only supported in linux") // The mask when all known capabilities are set. diff --git a/libbeat/common/capabilities/capabilities_other.go b/libbeat/common/capabilities/capabilities_other.go index 05ab64e01eb7..55b494c44974 100644 --- a/libbeat/common/capabilities/capabilities_other.go +++ b/libbeat/common/capabilities/capabilities_other.go @@ -21,7 +21,7 @@ package capabilities import "errors" -// Returned for all public functions on "not linux". +// ErrUnsupported is returned for all public functions on "not linux". var ErrUnsupported = errors.New("capabilities are only supported in linux") // Dummy value on "not linux". From 2f2e4447d8ddee4c34645bb5c3c7938ca7de62f1 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Thu, 11 Jan 2024 15:18:16 +0100 Subject: [PATCH 17/21] Handle SetFlag error It only errors out on in valid parameters, still makes the lint happier --- .../common/capabilities/capabilities_linux.go | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/libbeat/common/capabilities/capabilities_linux.go b/libbeat/common/capabilities/capabilities_linux.go index c9c2951bf93d..f3bf2fbc8afd 100644 --- a/libbeat/common/capabilities/capabilities_linux.go +++ b/libbeat/common/capabilities/capabilities_linux.go @@ -186,11 +186,29 @@ var isAll = makeIsAll() // so we have to build one. Instead of building it for every call, // build it once on startup and don't expose it. func makeIsAll() func(Flag, *cap.Set) (bool, error) { + var err error all := cap.NewSet() for i := 0; i < int(cap.MaxBits()); i++ { - all.SetFlag(cap.Effective, true, cap.Value(i)) - all.SetFlag(cap.Permitted, true, cap.Value(i)) - all.SetFlag(cap.Inheritable, true, cap.Value(i)) + err = all.SetFlag(cap.Effective, true, cap.Value(i)) + if err != nil { + break + } + err = all.SetFlag(cap.Permitted, true, cap.Value(i)) + if err != nil { + break + } + err = all.SetFlag(cap.Inheritable, true, cap.Value(i)) + if err != nil { + break + } + } + + // Building allset only fails with invalid parameters, handle + // it nonetheless + if err != nil { + return func(flag Flag, set *cap.Set) (bool, error) { + return false, err + } } return func(flag Flag, set *cap.Set) (bool, error) { From a008721f16069e032323c370bf283f071666e63d Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Fri, 26 Jan 2024 11:52:30 +0100 Subject: [PATCH 18/21] Remove CAP_ALL --- .../pkg/journalfield/conv_expand_test.go | 42 +++++++++- .../common/capabilities/capabilities_linux.go | 81 +++---------------- .../capabilities/capabilities_linux_test.go | 12 --- 3 files changed, 50 insertions(+), 85 deletions(-) diff --git a/filebeat/input/journald/pkg/journalfield/conv_expand_test.go b/filebeat/input/journald/pkg/journalfield/conv_expand_test.go index a3ff8867f63e..09daf7c8f5b6 100644 --- a/filebeat/input/journald/pkg/journalfield/conv_expand_test.go +++ b/filebeat/input/journald/pkg/journalfield/conv_expand_test.go @@ -123,7 +123,47 @@ var expandCapabilitiesTests = []struct { "thread": mapstr.M{ "capabilities": mapstr.M{ "effective": []string{ - "CAP_ALL", + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_DAC_READ_SEARCH", + "CAP_FOWNER", + "CAP_FSETID", + "CAP_KILL", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETPCAP", + "CAP_LINUX_IMMUTABLE", + "CAP_NET_BIND_SERVICE", + "CAP_NET_BROADCAST", + "CAP_NET_ADMIN", + "CAP_NET_RAW", + "CAP_IPC_LOCK", + "CAP_IPC_OWNER", + "CAP_SYS_MODULE", + "CAP_SYS_RAWIO", + "CAP_SYS_CHROOT", + "CAP_SYS_PTRACE", + "CAP_SYS_PACCT", + "CAP_SYS_ADMIN", + "CAP_SYS_BOOT", + "CAP_SYS_NICE", + "CAP_SYS_RESOURCE", + "CAP_SYS_TIME", + "CAP_SYS_TTY_CONFIG", + "CAP_MKNOD", + "CAP_LEASE", + "CAP_AUDIT_WRITE", + "CAP_AUDIT_CONTROL", + "CAP_SETFCAP", + "CAP_MAC_OVERRIDE", + "CAP_MAC_ADMIN", + "CAP_SYSLOG", + "CAP_WAKE_ALARM", + "CAP_BLOCK_SUSPEND", + "CAP_AUDIT_READ", + "CAP_PERFMON", + "CAP_BPF", + "CAP_CHECKPOINT_RESTORE", }, }, }, diff --git a/libbeat/common/capabilities/capabilities_linux.go b/libbeat/common/capabilities/capabilities_linux.go index f3bf2fbc8afd..59cf21920e1e 100644 --- a/libbeat/common/capabilities/capabilities_linux.go +++ b/libbeat/common/capabilities/capabilities_linux.go @@ -36,9 +36,6 @@ var ( // linux". There is a generic errors.ErrUnsupported present in // golang 1.21, but we still support 1.20. ErrUnsupported = errors.New("capabilities are only supported in linux") - - // The mask when all known capabilities are set. - allMask = (uint64(1) << uint64(cap.MaxBits())) - 1 ) // The capability set flag/vector, re-exported from @@ -55,8 +52,8 @@ const ( // Fetch the capabilities of pid for a given flag/vector and convert // it to the representation used in ECS. cap.GetPID() fetches it with -// SYS_CAPGET. Check FromUint64 for a definition of []{"CAP_ALL"}. -// May return ErrUnsupported on "not linux". +// SYS_CAPGET. +// Returns ErrUnsupported on "not linux". func FromPid(flag Flag, pid int) ([]string, error) { set, err := cap.GetPID(pid) if err != nil { @@ -69,15 +66,8 @@ func FromPid(flag Flag, pid int) ([]string, error) { if empty { return []string{}, nil } - all, err := isAll(flag, set) - if err != nil { - return nil, err - } - if all { - return []string{"CAP_ALL"}, nil - } - var sl []string + sl := make([]string, 0, cap.MaxBits()) for i := 0; i < int(cap.MaxBits()); i++ { c := cap.Value(i) enabled, err := set.GetFlag(flag, c) @@ -98,16 +88,9 @@ func FromPid(flag Flag, pid int) ([]string, error) { return sl, err } -// Convert a uint64 to the capabilities representation used in ECS. If -// all bits are set, []{"CAP_ALL"} is returned. The definition of what -// CAP_ALL is depends on the host as libcap(3) will probe the maximum -// number of capabilities on startup via cap.MaxBits(). -// May return ErrUnsupported on "not linux". +// Convert a uint64 to the capabilities representation used in ECS. +// Returns ErrUnsupported on "not linux". func FromUint64(w uint64) ([]string, error) { - if w == allMask { - return []string{"CAP_ALL"}, nil - } - sl := make([]string, 0, bits.OnesCount64(w)) for i := 0; w != 0; i++ { if w&1 != 0 { @@ -125,9 +108,8 @@ func FromUint64(w uint64) ([]string, error) { } // Convert a string to the capabilities representation used in -// ECS. Example input: "1ffffffffff", 16. See FromUint64 for details -// about CAP_ALL. -// May return ErrUnsupported on "not linux". +// ECS. Example input: "1ffffffffff", 16. +// Returns ErrUnsupported on "not linux". func FromString(s string, base int) ([]string, error) { w, err := strconv.ParseUint(s, 16, 64) if err != nil { @@ -178,52 +160,7 @@ func makeToECS() func(int) (string, error) { } } -// True if the set has all the capabilities set for the given -// flag/vector, see FromUint64 for a CAP_ALL explanation. -var isAll = makeIsAll() - -// Make isAll(), there is no direct way to get a full capability set, -// so we have to build one. Instead of building it for every call, -// build it once on startup and don't expose it. -func makeIsAll() func(Flag, *cap.Set) (bool, error) { - var err error - all := cap.NewSet() - for i := 0; i < int(cap.MaxBits()); i++ { - err = all.SetFlag(cap.Effective, true, cap.Value(i)) - if err != nil { - break - } - err = all.SetFlag(cap.Permitted, true, cap.Value(i)) - if err != nil { - break - } - err = all.SetFlag(cap.Inheritable, true, cap.Value(i)) - if err != nil { - break - } - } - - // Building allset only fails with invalid parameters, handle - // it nonetheless - if err != nil { - return func(flag Flag, set *cap.Set) (bool, error) { - return false, err - } - } - - return func(flag Flag, set *cap.Set) (bool, error) { - return isEqual(flag, set, all) - } -} - // Like isAll(), but for the empty set, here for symmetry. -var isEmpty = makeIsEmpty() - -// Make isEmpty(), the corollary to makeIsFull. -func makeIsEmpty() func(Flag, *cap.Set) (bool, error) { - empty := cap.NewSet() - - return func(flag Flag, set *cap.Set) (bool, error) { - return isEqual(flag, set, empty) - } +func isEmpty(flag Flag, set *cap.Set) (bool, error) { + return isEqual(flag, set, cap.NewSet()) } diff --git a/libbeat/common/capabilities/capabilities_linux_test.go b/libbeat/common/capabilities/capabilities_linux_test.go index 70e9bc9e0e43..1481fc5679b2 100644 --- a/libbeat/common/capabilities/capabilities_linux_test.go +++ b/libbeat/common/capabilities/capabilities_linux_test.go @@ -18,7 +18,6 @@ package capabilities import ( - "fmt" "os" "testing" @@ -47,17 +46,6 @@ func TestEmpty(t *testing.T) { } } -func TestAll(t *testing.T) { - all, err := FromString(fmt.Sprintf("%x", allMask), 16) - assert.Nil(t, err) - assert.Equal(t, all[0], "CAP_ALL") - - all, err = FromUint64(allMask) - assert.Nil(t, err) - assert.Equal(t, len(all), 1) - assert.Equal(t, all[0], "CAP_ALL") -} - func TestOverflow(t *testing.T) { sl, err := FromUint64(^uint64(0)) assert.Nil(t, err) From 1271f797bcca04ded45c81b784c4ecfe56b6bf35 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Fri, 26 Jan 2024 12:16:53 +0100 Subject: [PATCH 19/21] Progress has arrived, go 1.21 has a generic ErrUnsupported --- libbeat/common/capabilities/capabilities_linux.go | 11 +++-------- libbeat/common/capabilities/capabilities_other.go | 15 ++++++--------- x-pack/auditbeat/module/system/process/process.go | 4 ++-- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/libbeat/common/capabilities/capabilities_linux.go b/libbeat/common/capabilities/capabilities_linux.go index 59cf21920e1e..715b86d9bc7e 100644 --- a/libbeat/common/capabilities/capabilities_linux.go +++ b/libbeat/common/capabilities/capabilities_linux.go @@ -31,11 +31,6 @@ import ( var ( // errInvalidCapability expresses an invalid capability ID: x < 0 || x >= 64. errInvalidCapability = errors.New("invalid capability") - - // ErrUnsupported is returned for all public functions on "not - // linux". There is a generic errors.ErrUnsupported present in - // golang 1.21, but we still support 1.20. - ErrUnsupported = errors.New("capabilities are only supported in linux") ) // The capability set flag/vector, re-exported from @@ -53,7 +48,7 @@ const ( // Fetch the capabilities of pid for a given flag/vector and convert // it to the representation used in ECS. cap.GetPID() fetches it with // SYS_CAPGET. -// Returns ErrUnsupported on "not linux". +// Returns errors.ErrUnsupported on "not linux". func FromPid(flag Flag, pid int) ([]string, error) { set, err := cap.GetPID(pid) if err != nil { @@ -89,7 +84,7 @@ func FromPid(flag Flag, pid int) ([]string, error) { } // Convert a uint64 to the capabilities representation used in ECS. -// Returns ErrUnsupported on "not linux". +// Returns errors.ErrUnsupported on "not linux". func FromUint64(w uint64) ([]string, error) { sl := make([]string, 0, bits.OnesCount64(w)) for i := 0; w != 0; i++ { @@ -109,7 +104,7 @@ func FromUint64(w uint64) ([]string, error) { // Convert a string to the capabilities representation used in // ECS. Example input: "1ffffffffff", 16. -// Returns ErrUnsupported on "not linux". +// Returns errors.ErrUnsupported on "not linux". func FromString(s string, base int) ([]string, error) { w, err := strconv.ParseUint(s, 16, 64) if err != nil { diff --git a/libbeat/common/capabilities/capabilities_other.go b/libbeat/common/capabilities/capabilities_other.go index 55b494c44974..fbd7e8797728 100644 --- a/libbeat/common/capabilities/capabilities_other.go +++ b/libbeat/common/capabilities/capabilities_other.go @@ -21,9 +21,6 @@ package capabilities import "errors" -// ErrUnsupported is returned for all public functions on "not linux". -var ErrUnsupported = errors.New("capabilities are only supported in linux") - // Dummy value on "not linux". type Flag = uint @@ -34,17 +31,17 @@ const ( Permitted = Flag(1) ) -// ErrUnsupported on "not linux". +// Returns errors.ErrUnsupported on "not linux". func FromPid(flag Flag, pid int) ([]string, error) { - return nil, ErrUnsupported + return nil, errors.ErrUnsupported } -// ErrUnsupported on "not linux". +// Returns errors.ErrUnsupported on "not linux". func FromUint64(w uint64) ([]string, error) { - return nil, ErrUnsupported + return nil, errors.ErrUnsupported } -// ErrUnsupported on "not linux". +// Returns errors.ErrUnsupported on "not linux". func FromString(s string, base int) ([]string, error) { - return nil, ErrUnsupported + return nil, errors.ErrUnsupported } diff --git a/x-pack/auditbeat/module/system/process/process.go b/x-pack/auditbeat/module/system/process/process.go index c389476615ba..fca218eec41c 100644 --- a/x-pack/auditbeat/module/system/process/process.go +++ b/x-pack/auditbeat/module/system/process/process.go @@ -500,11 +500,11 @@ func (ms *MetricSet) getProcesses() ([]*Process, error) { } process.CapEffective, err = capabilities.FromPid(capabilities.Effective, pInfo.PID) - if err != nil && !errors.Is(err, capabilities.ErrUnsupported) && process.Error == nil { + if err != nil && !errors.Is(err, errors.ErrUnsupported) && process.Error == nil { process.Error = err } process.CapPermitted, err = capabilities.FromPid(capabilities.Permitted, pInfo.PID) - if err != nil && !errors.Is(err, capabilities.ErrUnsupported) && process.Error == nil { + if err != nil && !errors.Is(err, errors.ErrUnsupported) && process.Error == nil { process.Error = err } // Exclude Linux kernel processes, they are not very interesting. From 7b717d8bf8e89a5d5321862332d14cc6ce700006 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Fri, 26 Jan 2024 12:40:08 +0100 Subject: [PATCH 20/21] Don't bother fetching capabilities of kernel processes --- .../module/system/process/process.go | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/x-pack/auditbeat/module/system/process/process.go b/x-pack/auditbeat/module/system/process/process.go index fca218eec41c..08a72fe562e5 100644 --- a/x-pack/auditbeat/module/system/process/process.go +++ b/x-pack/auditbeat/module/system/process/process.go @@ -6,7 +6,6 @@ package process import ( "encoding/binary" - "errors" "fmt" "os" "os/user" @@ -499,17 +498,21 @@ func (ms *MetricSet) getProcesses() ([]*Process, error) { process.UserInfo = &userInfo } - process.CapEffective, err = capabilities.FromPid(capabilities.Effective, pInfo.PID) - if err != nil && !errors.Is(err, errors.ErrUnsupported) && process.Error == nil { - process.Error = err - } - process.CapPermitted, err = capabilities.FromPid(capabilities.Permitted, pInfo.PID) - if err != nil && !errors.Is(err, errors.ErrUnsupported) && process.Error == nil { - process.Error = err - } // Exclude Linux kernel processes, they are not very interesting. - if runtime.GOOS == "linux" && userInfo.UID == "0" && process.Info.Exe == "" { - continue + if runtime.GOOS == "linux" { + if userInfo.UID == "0" && process.Info.Exe == "" { + continue + } + + // Fetch Effective and Permitted capabilities + process.CapEffective, err = capabilities.FromPid(capabilities.Effective, pInfo.PID) + if err != nil && process.Error == nil { + process.Error = err + } + process.CapPermitted, err = capabilities.FromPid(capabilities.Permitted, pInfo.PID) + if err != nil && process.Error == nil { + process.Error = err + } } processes = append(processes, process) From 6b158f935f013e593cfd665f82fab1ec7a778082 Mon Sep 17 00:00:00 2001 From: Christiano Haesbaert Date: Mon, 29 Jan 2024 18:11:14 +0100 Subject: [PATCH 21/21] Update CHANGELOG.next.asciidoc Co-authored-by: Mattia Meleleo --- CHANGELOG.next.asciidoc | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 228189b98404..cc7f25a0086a 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -132,8 +132,6 @@ Setting environmental variable ELASTIC_NETINFO:false in Elastic Agent pod will d *Auditbeat* -- Add `ignore_errors` option to audit module. {issue}15768[15768] {pull}36851[36851] -- Fix copy arguments for strict aligned architectures. {pull}36976[36976] - Add linux capabilities to processes in the system/process. {pull}37453[37453] *Filebeat*