Skip to content

Commit

Permalink
Factorize code that resolve a cgroup path from a file
Browse files Browse the repository at this point in the history
  • Loading branch information
lebauce committed Dec 4, 2024
1 parent bce270c commit cb9b090
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 70 deletions.
17 changes: 3 additions & 14 deletions pkg/security/probe/field_handlers_ebpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ package probe
import (
"encoding/binary"
"path"
"path/filepath"
"strings"
"syscall"
"time"
Expand All @@ -21,6 +20,7 @@ import (
"github.com/DataDog/datadog-agent/pkg/security/resolvers"
sprocess "github.com/DataDog/datadog-agent/pkg/security/resolvers/process"
"github.com/DataDog/datadog-agent/pkg/security/secl/containerutils"
"github.com/DataDog/datadog-agent/pkg/security/seclog"

"github.com/DataDog/datadog-agent/pkg/security/secl/args"
"github.com/DataDog/datadog-agent/pkg/security/secl/model"
Expand Down Expand Up @@ -516,19 +516,8 @@ func (fh *EBPFFieldHandlers) ResolveCGroupID(ev *model.Event, e *model.CGroupCon
return string(entry.CGroup.CGroupID)
}

path, err := fh.resolvers.DentryResolver.Resolve(e.CGroupFile, true)
if err == nil && path != "" {
cgroup := filepath.Dir(string(path))
if cgroup == "/" {
cgroup = path
}

entry.Process.CGroup.CGroupID = containerutils.CGroupID(cgroup)
entry.CGroup.CGroupID = containerutils.CGroupID(cgroup)
entry.CGroup.CGroupFile = e.CGroupFile
containerID, _ := containerutils.GetContainerFromCgroup(entry.CGroup.CGroupID)
entry.Process.ContainerID = containerutils.ContainerID(containerID)
entry.ContainerID = containerutils.ContainerID(containerID)
if err := fh.resolvers.ResolveCGroup(entry, ev.BaseEvent.PIDContext.Pid, e.CGroupFile, e.CGroupFlags, nil); err != nil {
seclog.Debugf("Failed to resolve cgroup: %s", err)
}

e.CGroupID = entry.CGroup.CGroupID
Expand Down
25 changes: 2 additions & 23 deletions pkg/security/probe/probe_ebpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -824,30 +824,9 @@ func (p *EBPFProbe) handleEvent(CPU int, data []byte) {

pce := p.Resolvers.ProcessResolver.Resolve(event.CgroupWrite.Pid, event.CgroupWrite.Pid, 0, false, newEntryCb)
if pce != nil {
path, err := p.Resolvers.DentryResolver.Resolve(event.CgroupWrite.File.PathKey, true)
if err == nil && path != "" {
if !p.kernelVersion.IsRH7Kernel() {
path = filepath.Dir(string(path))
}

cgroupID := containerutils.CGroupID(path)
pce.CGroup.CGroupID = cgroupID
pce.Process.CGroup.CGroupID = cgroupID
pce.CGroup.CGroupFile = event.CgroupWrite.File.FileFields.PathKey
pce.Process.CGroup.CGroupFile = event.CgroupWrite.File.FileFields.PathKey
cgroupFlags := containerutils.CGroupFlags(event.CgroupWrite.CGroupFlags)
if cgroupFlags.IsContainer() {
containerID, _ := containerutils.GetContainerFromCgroup(cgroupID)
pce.ContainerID = containerutils.ContainerID(containerID)
pce.Process.ContainerID = containerutils.ContainerID(containerID)
}
pce.CGroup.CGroupFlags = cgroupFlags
pce.Process.CGroup = pce.CGroup
} else {
seclog.Debugf("failed to resolve cgroup file %v", event.CgroupWrite.File)
if err := p.Resolvers.ResolveCGroup(pce, event.CgroupWrite.Pid, event.CgroupWrite.File.PathKey, containerutils.CGroupFlags(event.CgroupWrite.CGroupFlags), newEntryCb); err != nil {
seclog.Debugf("Failed to resolve cgroup: %s", err)
}
} else {
seclog.Debugf("failed to resolve process of cgroup write event: %s", err)
}
return
case model.UnshareMountNsEventType:
Expand Down
2 changes: 1 addition & 1 deletion pkg/security/ptracer/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func getContainerIDFromProcFS(cgroupPath string) (containerutils.ContainerID, er
}

for _, cgroup := range cgroups {
if cid, _ := containerutils.FindContainerID(cgroup.path); cid != "" {
if cid, _ := containerutils.FindContainerID(containerutils.CGroupID(cgroup.path)); cid != "" {
return cid, nil
}
}
Expand Down
34 changes: 34 additions & 0 deletions pkg/security/resolvers/resolvers_ebpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"context"
"fmt"
"os"
"path/filepath"
"sort"

"github.com/DataDog/datadog-go/v5/statsd"
Expand All @@ -37,6 +38,8 @@ import (
"github.com/DataDog/datadog-agent/pkg/security/resolvers/tc"
"github.com/DataDog/datadog-agent/pkg/security/resolvers/usergroup"
"github.com/DataDog/datadog-agent/pkg/security/resolvers/usersessions"
"github.com/DataDog/datadog-agent/pkg/security/secl/containerutils"
"github.com/DataDog/datadog-agent/pkg/security/secl/model"
"github.com/DataDog/datadog-agent/pkg/security/utils"
"github.com/DataDog/datadog-agent/pkg/util/ktime"
"github.com/DataDog/datadog-agent/pkg/util/log"
Expand Down Expand Up @@ -214,6 +217,37 @@ func (r *EBPFResolvers) Start(ctx context.Context) error {
return r.NamespaceResolver.Start(ctx)
}

// ResolveCGroup resolves the path of cgroup for a process cache entry
func (r *EBPFResolvers) ResolveCGroup(pce *model.ProcessCacheEntry, pid uint32, pathKey model.PathKey, cgroupFlags containerutils.CGroupFlags, newEntryCb func(entry *model.ProcessCacheEntry, err error)) error {
path, err := r.DentryResolver.Resolve(pathKey, true)
if err == nil && path != "" {
cgroup := filepath.Dir(string(path))
if cgroup == "/" {
cgroup = path
}

cgroupFlags := containerutils.CGroupFlags(cgroupFlags)
cgroupContext := model.CGroupContext{
CGroupID: containerutils.CGroupID(cgroup),
CGroupFlags: containerutils.CGroupFlags(cgroupFlags),
CGroupFile: pathKey,
}

pce.Process.CGroup = cgroupContext
pce.CGroup = cgroupContext

if cgroupFlags.IsContainer() {
containerID, _ := containerutils.FindContainerID(cgroupContext.CGroupID)
pce.ContainerID = containerID
pce.Process.ContainerID = containerID
}
} else {
return fmt.Errorf("failed to resolve cgroup file %v: %w", pathKey, err)
}

return nil
}

// Snapshot collects data on the current state of the system to populate user space and kernel space caches.
func (r *EBPFResolvers) Snapshot() error {
if err := r.snapshot(); err != nil {
Expand Down
36 changes: 14 additions & 22 deletions pkg/security/secl/containerutils/cgroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,31 +36,23 @@ const (
)

// RuntimePrefixes holds the cgroup prefixed used by the different runtimes
var RuntimePrefixes = map[string]CGroupManager{
"docker/": CGroupManagerDocker, // On Amazon Linux 2 with Docker, 'docker' is the folder name and not a prefix
"docker-": CGroupManagerDocker,
"cri-containerd-": CGroupManagerCRI,
"crio-": CGroupManagerCRIO,
"libpod-": CGroupManagerPodman,
var RuntimePrefixes = []struct {
prefix string
flags CGroupManager
}{
{"docker/", CGroupManagerDocker}, // On Amazon Linux 2 with Docker, 'docker' is the folder name and not a prefix
{"docker-", CGroupManagerDocker},
{"cri-containerd-", CGroupManagerCRI},
{"crio-", CGroupManagerCRIO},
{"libpod-", CGroupManagerPodman},
}

// GetCGroupManager extracts the cgroup manager from a cgroup name
func GetCGroupManager(cgroup string) (string, CGroupFlags) {
cgroup = strings.TrimLeft(cgroup, "/")
for runtimePrefix, runtimeFlag := range RuntimePrefixes {
if strings.HasPrefix(cgroup, runtimePrefix) {
return cgroup[:len(runtimePrefix)], CGroupFlags(runtimeFlag)
}
}
return cgroup, 0
}

// GetContainerFromCgroup extracts the container ID from a cgroup name
func GetContainerFromCgroup(cgroup CGroupID) (ContainerID, CGroupFlags) {
// getContainerFromCgroup extracts the container ID from a cgroup name
func getContainerFromCgroup(cgroup CGroupID) (ContainerID, CGroupFlags) {
cgroupID := strings.TrimLeft(string(cgroup), "/")
for runtimePrefix, runtimeFlag := range RuntimePrefixes {
if strings.HasPrefix(cgroupID, runtimePrefix) {
return ContainerID(cgroupID[len(runtimePrefix):]), CGroupFlags(runtimeFlag)
for _, runtimePrefix := range RuntimePrefixes {
if strings.HasPrefix(cgroupID, runtimePrefix.prefix) {
return ContainerID(cgroupID[len(runtimePrefix.prefix):]), CGroupFlags(runtimePrefix.flags)
}
}
return "", 0
Expand Down
12 changes: 6 additions & 6 deletions pkg/security/secl/containerutils/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,19 @@ var containerIDCoreChars = "0123456789abcdefABCDEF"

func init() {
var prefixes []string
for prefix := range RuntimePrefixes {
prefixes = append(prefixes, prefix)
for _, runtimePrefix := range RuntimePrefixes {
prefixes = append(prefixes, runtimePrefix.prefix)
}
ContainerIDPatternStr = "(?:" + strings.Join(prefixes[:], "|") + ")?([0-9a-fA-F]{64})|([0-9a-fA-F]{32}-\\d+)|([0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){4})"
containerIDPattern = regexp.MustCompile(ContainerIDPatternStr)
}

func isSystemdCgroup(cgroup string) bool {
return strings.HasSuffix(cgroup, ".service") || strings.HasSuffix(cgroup, ".scope")
func isSystemdCgroup(cgroup CGroupID) bool {
return strings.HasSuffix(string(cgroup), ".service") || strings.HasSuffix(string(cgroup), ".scope")
}

// FindContainerID extracts the first sub string that matches the pattern of a container ID along with the container flags induced from the container runtime prefix
func FindContainerID(s string) (ContainerID, uint64) {
func FindContainerID(s CGroupID) (ContainerID, uint64) {
match := containerIDPattern.FindIndex([]byte(s))
if match == nil {
if isSystemdCgroup(s) {
Expand Down Expand Up @@ -69,7 +69,7 @@ func FindContainerID(s string) (ContainerID, uint64) {
// it starts or/and ends the initial string

cgroupID := s[match[0]:match[1]]
containerID, flags := GetContainerFromCgroup(CGroupID(cgroupID))
containerID, flags := getContainerFromCgroup(CGroupID(cgroupID))
if containerID == "" {
return ContainerID(cgroupID), uint64(flags)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/security/secl/containerutils/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func TestFindContainerID(t *testing.T) {
}

for _, test := range testCases {
containerID, containerFlags := FindContainerID(test.input)
containerID, containerFlags := FindContainerID(CGroupID(test.input))
assert.Equal(t, test.output, string(containerID))
assert.Equal(t, uint64(test.flags), containerFlags, "wrong flags for container %s", containerID)
}
Expand Down
5 changes: 4 additions & 1 deletion pkg/security/secl/model/process_cache_entry_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,13 @@ func copyProcessContext(parent, child *ProcessCacheEntry) {
// WARNING: this is why the user space cache should not be used to detect container breakouts. Dedicated
// in-kernel probes will need to be added.
if len(parent.ContainerID) > 0 && len(child.ContainerID) == 0 {
child.CGroup = parent.CGroup
child.ContainerID = parent.ContainerID
}

if len(parent.CGroup.CGroupID) > 0 && len(child.CGroup.CGroupID) == 0 {
child.CGroup = parent.CGroup
}

// AUIDs should be inherited just like container IDs
child.Credentials.AUID = parent.Credentials.AUID
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/security/utils/cgroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ type ControlGroup struct {

// GetContainerContext returns both the container ID and its flags
func (cg ControlGroup) GetContainerContext() (containerutils.ContainerID, containerutils.CGroupFlags) {
id, flags := containerutils.FindContainerID(cg.Path)
id, flags := containerutils.FindContainerID(containerutils.CGroupID(cg.Path))
return containerutils.ContainerID(id), containerutils.CGroupFlags(flags)
}

// GetContainerID returns the container id extracted from the path of the control group
func (cg ControlGroup) GetContainerID() containerutils.ContainerID {
id, _ := containerutils.FindContainerID(cg.Path)
id, _ := containerutils.FindContainerID(containerutils.CGroupID(cg.Path))
return containerutils.ContainerID(id)
}

Expand Down

0 comments on commit cb9b090

Please sign in to comment.