Skip to content

Commit

Permalink
Merge pull request #338 from kubescape/feature/rules-improvements
Browse files Browse the repository at this point in the history
Feature/rules improvements
  • Loading branch information
amitschendel authored Aug 14, 2024
2 parents b25a871 + 9c59223 commit 841ea43
Show file tree
Hide file tree
Showing 22 changed files with 1,081 additions and 215 deletions.
21 changes: 21 additions & 0 deletions pkg/containerwatcher/v1/container_watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import (
tracerhardlinktype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/hardlink/types"
tracerandomx "github.com/kubescape/node-agent/pkg/ebpf/gadgets/randomx/tracer"
tracerandomxtype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/randomx/types"
tracerssh "github.com/kubescape/node-agent/pkg/ebpf/gadgets/ssh/tracer"
tracersshtype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/ssh/types"
tracersymlink "github.com/kubescape/node-agent/pkg/ebpf/gadgets/symlink/tracer"
tracersymlinktype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/symlink/types"
"github.com/kubescape/node-agent/pkg/malwaremanager"
Expand All @@ -55,6 +57,7 @@ const (
randomxTraceName = "trace_randomx"
symlinkTraceName = "trace_symlink"
hardlinkTraceName = "trace_hardlink"
sshTraceName = "trace_ssh"
capabilitiesWorkerPoolSize = 1
execWorkerPoolSize = 2
openWorkerPoolSize = 8
Expand All @@ -63,6 +66,7 @@ const (
randomxWorkerPoolSize = 1
symlinkWorkerPoolSize = 1
hardlinkWorkerPoolSize = 1
sshWorkerPoolSize = 1
)

type IGContainerWatcher struct {
Expand Down Expand Up @@ -98,6 +102,7 @@ type IGContainerWatcher struct {
randomxTracer *tracerandomx.Tracer
symlinkTracer *tracersymlink.Tracer
hardlinkTracer *tracerhardlink.Tracer
sshTracer *tracerssh.Tracer
kubeIPInstance operators.OperatorInstance
kubeNameInstance operators.OperatorInstance

Expand All @@ -110,6 +115,7 @@ type IGContainerWatcher struct {
randomxWorkerPool *ants.PoolWithFunc
symlinkWorkerPool *ants.PoolWithFunc
hardlinkWorkerPool *ants.PoolWithFunc
sshdWorkerPool *ants.PoolWithFunc

capabilitiesWorkerChan chan *tracercapabilitiestype.Event
execWorkerChan chan *tracerexectype.Event
Expand All @@ -119,6 +125,7 @@ type IGContainerWatcher struct {
randomxWorkerChan chan *tracerandomxtype.Event
symlinkWorkerChan chan *tracersymlinktype.Event
hardlinkWorkerChan chan *tracerhardlinktype.Event
sshWorkerChan chan *tracersshtype.Event

preRunningContainersIDs mapset.Set[string]

Expand Down Expand Up @@ -293,6 +300,18 @@ func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager appli
if err != nil {
return nil, fmt.Errorf("creating hardlink worker pool: %w", err)
}
// Create a ssh worker pool
sshWorkerPool, err := ants.NewPoolWithFunc(sshWorkerPoolSize, func(i interface{}) {
event := i.(tracersshtype.Event)
if event.K8s.ContainerName == "" {
return
}
metrics.ReportEvent(utils.SSHEventType)
ruleManager.ReportSSHEvent(event)
})
if err != nil {
return nil, fmt.Errorf("creating ssh worker pool: %w", err)
}

return &IGContainerWatcher{
// Configuration
Expand Down Expand Up @@ -323,6 +342,7 @@ func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager appli
randomxWorkerPool: randomxWorkerPool,
symlinkWorkerPool: symlinkWorkerPool,
hardlinkWorkerPool: hardlinkWorkerPool,
sshdWorkerPool: sshWorkerPool,
metrics: metrics,
preRunningContainersIDs: preRunningContainers,

Expand All @@ -335,6 +355,7 @@ func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager appli
randomxWorkerChan: make(chan *tracerandomxtype.Event, 5000),
symlinkWorkerChan: make(chan *tracersymlinktype.Event, 1000),
hardlinkWorkerChan: make(chan *tracerhardlinktype.Event, 1000),
sshWorkerChan: make(chan *tracersshtype.Event, 1000),

// cache
ruleBindingPodNotify: ruleBindingPodNotify,
Expand Down
12 changes: 12 additions & 0 deletions pkg/containerwatcher/v1/container_watcher_private.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,12 @@ func (ch *IGContainerWatcher) startTracers() error {
logger.L().Error("error starting hardlink tracing", helpers.Error(err))
return err
}

// NOTE: SSH tracing relies on the network tracer, so it must be started after the network tracer.
if err := ch.startSshTracing(); err != nil {
logger.L().Error("error starting ssh tracing", helpers.Error(err))
return err
}
}

return nil
Expand Down Expand Up @@ -322,6 +328,12 @@ func (ch *IGContainerWatcher) stopTracers() error {
logger.L().Error("error stopping hardlink tracing", helpers.Error(err))
errs = errors.Join(errs, err)
}

// Stop ssh tracer
if err := ch.stopSshTracing(); err != nil {
logger.L().Error("error stopping ssh tracing", helpers.Error(err))
errs = errors.Join(errs, err)
}
}

return errs
Expand Down
78 changes: 78 additions & 0 deletions pkg/containerwatcher/v1/ssh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package containerwatcher

import (
"fmt"

tracerssh "github.com/kubescape/node-agent/pkg/ebpf/gadgets/ssh/tracer"
tracersshtype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/ssh/types"

"github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection/networktracer"
"github.com/inspektor-gadget/inspektor-gadget/pkg/types"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
)

func (ch *IGContainerWatcher) sshEventCallback(event *tracersshtype.Event) {
if event.Type == types.DEBUG {
return
}

if isDroppedEvent(event.Type, event.Message) {
logger.L().Ctx(ch.ctx).Warning("ssh tracer got drop events - we may miss some realtime data", helpers.Interface("event", event), helpers.String("error", event.Message))
return
}

ch.containerCollection.EnrichByMntNs(&event.CommonData, event.MountNsID)
ch.containerCollection.EnrichByNetNs(&event.CommonData, event.NetNsID)

ch.sshWorkerChan <- event
}

func (ch *IGContainerWatcher) startSshTracing() error {
if err := ch.tracerCollection.AddTracer(sshTraceName, ch.containerSelector); err != nil {
return fmt.Errorf("adding tracer: %w", err)
}

tracerSsh, err := tracerssh.NewTracer()
if err != nil {
return fmt.Errorf("creating tracer: %w", err)
}
go func() {
for event := range ch.sshWorkerChan {
_ = ch.sshdWorkerPool.Invoke(*event)
}
}()

tracerSsh.SetSocketEnricherMap(ch.socketEnricher.SocketsMap())
tracerSsh.SetEventHandler(ch.sshEventCallback)

err = tracerSsh.RunWorkaround()
if err != nil {
return fmt.Errorf("running workaround: %w", err)
}

ch.sshTracer = tracerSsh

config := &networktracer.ConnectToContainerCollectionConfig[tracersshtype.Event]{
Tracer: ch.sshTracer,
Resolver: ch.containerCollection,
Selector: ch.containerSelector,
Base: tracersshtype.Base,
}

_, err = networktracer.ConnectToContainerCollection(config)
if err != nil {
return fmt.Errorf("creating tracer: %w", err)
}

return nil
}

func (ch *IGContainerWatcher) stopSshTracing() error {
// Stop ssh tracer
if err := ch.tracerCollection.RemoveTracer(sshTraceName); err != nil {
return fmt.Errorf("removing tracer: %w", err)
}
ch.sshTracer.Close()
return nil
}
108 changes: 108 additions & 0 deletions pkg/ebpf/gadgets/ssh/tracer/bpf/ssh.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/in.h>
#include <linux/types.h>
#include <sys/socket.h>
#include <stdbool.h>

#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>

#include "../../../../include/macros.h"
#include "../../../../include/buffer.h"

#define GADGET_TYPE_NETWORKING
#include "../../../../include/sockets-map.h"

#include "ssh.h"


// Events map.
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(__u32));
__uint(value_size, sizeof(__u32));
} events SEC(".maps");

// Empty event map.
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 1);
__type(key, __u32);
__type(value, struct event);
} empty_event SEC(".maps");

// we need this to make sure the compiler doesn't remove our struct.
const struct event *unusedevent __attribute__((unused));

SEC("socket")
int ssh_detector(struct __sk_buff *skb) {
// Check if it's an IP packet
if (skb->protocol != bpf_htons(ETH_P_IP))
return 0;

// Define the offset for IP header
int ip_offset = ETH_HLEN;

// Read IP header
struct iphdr iph;
if (bpf_skb_load_bytes(skb, ip_offset, &iph, sizeof(iph)) < 0)
return 0;

// Check if it's a TCP packet
if (iph.protocol != IPPROTO_TCP)
return 0;

// Calculate TCP header offset
int tcp_offset = ip_offset + (iph.ihl * 4);

// Read TCP header
struct tcphdr tcph;
if (bpf_skb_load_bytes(skb, tcp_offset, &tcph, sizeof(tcph)) < 0)
return 0;

// Calculate payload offset
int payload_offset = tcp_offset + (tcph.doff * 4);

// Read the first 4 bytes of the payload
char payload[SSH_SIG_LEN];
if (bpf_skb_load_bytes(skb, payload_offset, payload, SSH_SIG_LEN) < 0)
return 0;

// Check for SSH signature using memcmp
if (__builtin_memcmp(payload, SSH_SIGNATURE, SSH_SIG_LEN) == 0) {
struct event *event;
__u32 zero = 0;
event = bpf_map_lookup_elem(&empty_event, &zero);
if (!event) {
return 0;
}

// Enrich event with process metadata
struct sockets_value *skb_val = gadget_socket_lookup(skb);
if (skb_val != NULL) {
event->netns = skb->cb[0]; // cb[0] initialized by dispatcher.bpf.c
event->mntns_id = skb_val->mntns;
event->pid = skb_val->pid_tgid >> 32;
event->uid = (__u32)(skb_val->uid_gid);
event->gid = (__u32)(skb_val->uid_gid >> 32);
__builtin_memcpy(&event->comm, skb_val->task, sizeof(event->comm));

event->src_ip = iph.saddr;
event->dst_ip = iph.daddr;
event->src_port = bpf_ntohs(tcph.source);
event->dst_port = bpf_ntohs(tcph.dest);

event->timestamp = bpf_ktime_get_boot_ns();
}
__u64 skb_len = skb->len;
bpf_perf_event_output(skb, &events, skb_len << 32 | BPF_F_CURRENT_CPU, event, sizeof(struct event));
}

return 0;
}


char LICENSE[] SEC("license") = "Dual BSD/GPL";
33 changes: 33 additions & 0 deletions pkg/ebpf/gadgets/ssh/tracer/bpf/ssh.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include "../../../../include/types.h"

#ifndef TASK_COMM_LEN
#define TASK_COMM_LEN 16
#endif
#define INVALID_UID ((uid_t)-1)
// Defined in include/uapi/linux/magic.h
#define OVERLAYFS_SUPER_MAGIC 0x794c7630
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
#define SSH_SIGNATURE "SSH-"
#define SSH_SIG_LEN 4
#define ETH_P_IP 0x0800 /* Internet Protocol packet */
#define ETH_HLEN 14

struct event {
// Keep netns at the top: networktracer depends on it
__u32 netns;

gadget_timestamp timestamp;
gadget_mntns_id mntns_id;
__u32 pid;
__u32 uid;
__u32 gid;
__u16 dst_port;
__u16 src_port;
__u32 dst_ip;
__u32 src_ip;
__u8 comm[TASK_COMM_LEN];
};
Loading

0 comments on commit 841ea43

Please sign in to comment.