Skip to content

Commit

Permalink
[CWS] Snapshot and runtime use the same file to identify cgroups (#32693
Browse files Browse the repository at this point in the history
)

Co-authored-by: lebauce <[email protected]>
  • Loading branch information
YoannGh and lebauce authored Jan 9, 2025
1 parent 9ac7eb4 commit 8a65880
Show file tree
Hide file tree
Showing 7 changed files with 360 additions and 54 deletions.
9 changes: 5 additions & 4 deletions pkg/security/ebpf/c/include/hooks/cgroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,15 @@ static __attribute__((always_inline)) int trace__cgroup_write(ctx_t *ctx) {
bpf_probe_read(&f, sizeof(f), &kern_f->file);
struct dentry *dentry = get_file_dentry(f);

resolver->key.ino = get_dentry_ino(dentry);
resolver->key.mount_id = get_file_mount_id(f);
resolver->dentry = dentry;

// The last dentry in the cgroup path should be `cgroup.procs`, thus the container ID should be its parent.
bpf_probe_read(&container_d, sizeof(container_d), &dentry->d_parent);
bpf_probe_read(&container_qstr, sizeof(container_qstr), &container_d->d_name);
container_id = (void *)container_qstr.name;

resolver->key.ino = get_dentry_ino(container_d);
resolver->key.mount_id = get_file_mount_id(f);
resolver->dentry = container_d;

if (is_docker_cgroup(ctx, container_d)) {
cgroup_flags = CGROUP_MANAGER_DOCKER;
}
Expand All @@ -133,6 +133,7 @@ static __attribute__((always_inline)) int trace__cgroup_write(ctx_t *ctx) {

u64 inode = get_dentry_ino(container_d);
resolver->key.ino = inode;

struct file_t *entry = bpf_map_lookup_elem(&exec_file_cache, &inode);
if (entry == NULL) {
return 0;
Expand Down
19 changes: 14 additions & 5 deletions pkg/security/resolvers/container/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,19 @@ import (
)

// Resolver is used to resolve the container context of the events
type Resolver struct{}
type Resolver struct {
fs *utils.CGroupFS
}

// New creates a new container resolver
func New() *Resolver {
return &Resolver{
fs: utils.NewCGroupFS(),
}
}

// GetContainerContext returns the container id of the given pid along with its flags
func (cr *Resolver) GetContainerContext(pid uint32) (containerutils.ContainerID, model.CGroupContext, error) {
// Parse /proc/[pid]/task/[pid]/cgroup
return utils.GetProcContainerContext(pid, pid)
// GetContainerContext returns the container id, cgroup context, and cgroup sysfs path of the given pid
func (cr *Resolver) GetContainerContext(pid uint32) (containerutils.ContainerID, model.CGroupContext, string, error) {
// Parse /proc/[pid]/task/[pid]/cgroup and /sys/fs/cgroup/[cgroup]
return cr.fs.FindCGroupContext(pid, pid)
}
27 changes: 13 additions & 14 deletions pkg/security/resolvers/process/resolver_ebpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,12 +375,22 @@ func (p *EBPFResolver) enrichEventFromProc(entry *model.ProcessCacheEntry, proc
return fmt.Errorf("snapshot failed for %d: couldn't retrieve inode info: %w", proc.Pid, err)
}

// Retrieve the container ID of the process from /proc
containerID, cgroup, err := p.containerResolver.GetContainerContext(pid)
// Retrieve the container ID of the process from /proc and /sys/fs/cgroup/[cgroup]
containerID, cgroup, cgroupSysFSPath, err := p.containerResolver.GetContainerContext(pid)
if err != nil {
return fmt.Errorf("snapshot failed for %d: couldn't parse container and cgroup context: %w", proc.Pid, err)
}

if cgroup.CGroupFile.Inode != 0 && cgroup.CGroupFile.MountID == 0 { // the mount id is unavailable through statx
// Get the file fields of the sysfs cgroup file
info, err := p.retrieveExecFileFields(cgroupSysFSPath)
if err != nil && !errors.Is(err, lib.ErrKeyNotExist) {
seclog.Debugf("snapshot failed for %d: couldn't retrieve inode info: %s", proc.Pid, err)
} else {
cgroup.CGroupFile.MountID = info.MountID
}
}

entry.ContainerID = containerID

entry.CGroup = cgroup
Expand All @@ -393,17 +403,6 @@ func (p *EBPFResolver) enrichEventFromProc(entry *model.ProcessCacheEntry, proc
entry.FileEvent.MountOrigin = model.MountOriginProcfs
entry.FileEvent.MountSource = model.MountSourceSnapshot

if entry.Process.CGroup.CGroupFile.MountID == 0 {
// Get the file fields of the cgroup file
taskPath := utils.CgroupTaskPath(pid, pid)
info, err := p.retrieveExecFileFields(taskPath)
if err != nil {
seclog.Debugf("snapshot failed for %d: couldn't retrieve inode info: %s", proc.Pid, err)
} else {
entry.Process.CGroup.CGroupFile.MountID = info.MountID
}
}

if entry.FileEvent.IsFileless() {
entry.FileEvent.Filesystem = model.TmpFS
} else {
Expand Down Expand Up @@ -892,7 +891,7 @@ func (p *EBPFResolver) resolveFromKernelMaps(pid, tid uint32, inode uint64, newE
// the parent is in a container. In other words, we have to fall back to /proc to query the container ID of the
// process.
if entry.CGroup.CGroupFile.Inode == 0 {
if containerID, cgroup, err := p.containerResolver.GetContainerContext(pid); err == nil {
if containerID, cgroup, _, err := p.containerResolver.GetContainerContext(pid); err == nil {
entry.CGroup.Merge(&cgroup)
entry.ContainerID = containerID
}
Expand Down
10 changes: 2 additions & 8 deletions pkg/security/resolvers/resolvers_ebpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"context"
"fmt"
"os"
"path/filepath"
"sort"

"github.com/DataDog/datadog-go/v5/statsd"
Expand Down Expand Up @@ -133,7 +132,7 @@ func NewEBPFResolvers(config *config.Config, manager *manager.Manager, statsdCli
mountResolver = &mount.NoOpResolver{}
pathResolver = &path.NoOpResolver{}
}
containerResolver := &container.Resolver{}
containerResolver := container.New()

processOpts := process.NewResolverOpts()
processOpts.WithEnvsValue(config.Probe.EnvsWithValue)
Expand Down Expand Up @@ -223,16 +222,11 @@ func (r *EBPFResolvers) ResolveCGroupContext(pathKey model.PathKey, cgroupFlags
return cgroupContext, nil
}

path, err := r.DentryResolver.Resolve(pathKey, true)
cgroup, err := r.DentryResolver.Resolve(pathKey, true)
if err != nil {
return nil, fmt.Errorf("failed to resolve cgroup file %v: %w", pathKey, err)
}

cgroup := filepath.Dir(string(path))
if cgroup == "/" {
cgroup = path
}

cgroupContext := &model.CGroupContext{
CGroupID: containerutils.CGroupID(cgroup),
CGroupFlags: containerutils.CGroupFlags(cgroupFlags),
Expand Down
8 changes: 3 additions & 5 deletions pkg/security/resolvers/resolvers_ebpfless.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,16 @@ import (
"github.com/DataDog/datadog-agent/pkg/process/procutil"
"github.com/DataDog/datadog-agent/pkg/security/config"
"github.com/DataDog/datadog-agent/pkg/security/resolvers/cgroup"
"github.com/DataDog/datadog-agent/pkg/security/resolvers/container"
"github.com/DataDog/datadog-agent/pkg/security/resolvers/hash"
"github.com/DataDog/datadog-agent/pkg/security/resolvers/process"
"github.com/DataDog/datadog-agent/pkg/security/resolvers/tags"
)

// EBPFLessResolvers holds the list of the event attribute resolvers
type EBPFLessResolvers struct {
ContainerResolver *container.Resolver
TagsResolver *tags.LinuxResolver
ProcessResolver *process.EBPFLessResolver
HashResolver *hash.Resolver
TagsResolver *tags.LinuxResolver
ProcessResolver *process.EBPFLessResolver
HashResolver *hash.Resolver
}

// NewEBPFLessResolvers creates a new instance of EBPFLessResolvers
Expand Down
Loading

0 comments on commit 8a65880

Please sign in to comment.