Skip to content

Commit

Permalink
fix: ebpf capture: attach programs on tc hooks after pds created; ena…
Browse files Browse the repository at this point in the history
…ble TLS interception in containerized k8s like minikube and kind (#52)

* fix: ebpf capture: attach programs on tc hooks after pds created

* enable TLS interception for containerized k8s like minikube and kind

* enable TLS interception for containerized k8s like minikube and kind

---------

Co-authored-by: Alon Girmonsky <1990761+alongir@users.noreply.github.com>
  • Loading branch information
iluxa and alongir committed Apr 24, 2024
1 parent b4fee75 commit 40334a3
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 42 deletions.
8 changes: 4 additions & 4 deletions bpf/fd_to_address_tracepoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ struct sys_enter_accept4_ctx {

SEC("tracepoint/syscalls/sys_enter_accept4")
void sys_enter_accept4(struct sys_enter_accept4_ctx* ctx) {
__u64 id = bpf_get_current_pid_tgid();
__u64 id = tracer_get_current_pid_tgid();

if (!should_watch(id >> 32)) {
return;
Expand All @@ -55,7 +55,7 @@ struct sys_exit_accept4_ctx {

SEC("tracepoint/syscalls/sys_exit_accept4")
void sys_exit_accept4(struct sys_exit_accept4_ctx* ctx) {
__u64 id = bpf_get_current_pid_tgid();
__u64 id = tracer_get_current_pid_tgid();

if (!should_watch(id >> 32)) {
return;
Expand Down Expand Up @@ -122,7 +122,7 @@ struct sys_enter_connect_ctx {

SEC("tracepoint/syscalls/sys_enter_connect")
void sys_enter_connect(struct sys_enter_connect_ctx* ctx) {
__u64 id = bpf_get_current_pid_tgid();
__u64 id = tracer_get_current_pid_tgid();

if (!should_watch(id >> 32)) {
return;
Expand All @@ -149,7 +149,7 @@ struct sys_exit_connect_ctx {

SEC("tracepoint/syscalls/sys_exit_connect")
void sys_exit_connect(struct sys_exit_connect_ctx* ctx) {
__u64 id = bpf_get_current_pid_tgid();
__u64 id = tracer_get_current_pid_tgid();

if (!should_watch(id >> 32)) {
return;
Expand Down
8 changes: 4 additions & 4 deletions bpf/fd_tracepoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ static __always_inline void fd_tracepoints_handle_go(struct sys_enter_read_write

SEC("tracepoint/syscalls/sys_enter_read")
void sys_enter_read(struct sys_enter_read_write_ctx* ctx) {
__u64 id = bpf_get_current_pid_tgid();
__u64 id = tracer_get_current_pid_tgid();

if (!should_watch(id >> 32)) {
return;
Expand All @@ -76,7 +76,7 @@ void sys_enter_read(struct sys_enter_read_write_ctx* ctx) {

SEC("tracepoint/syscalls/sys_enter_write")
void sys_enter_write(struct sys_enter_read_write_ctx* ctx) {
__u64 id = bpf_get_current_pid_tgid();
__u64 id = tracer_get_current_pid_tgid();

if (!should_watch(id >> 32)) {
return;
Expand All @@ -93,15 +93,15 @@ void sys_enter_write(struct sys_enter_read_write_ctx* ctx) {

SEC("tracepoint/syscalls/sys_exit_read")
void sys_exit_read(struct sys_exit_read_write_ctx* ctx) {
__u64 id = bpf_get_current_pid_tgid();
__u64 id = tracer_get_current_pid_tgid();
// Delete from go map. The value is not used after exiting this syscall.
// Keep value in openssl map.
bpf_map_delete_elem(&go_kernel_read_context, &id);
}

SEC("tracepoint/syscalls/sys_exit_write")
void sys_exit_write(struct sys_exit_read_write_ctx* ctx) {
__u64 id = bpf_get_current_pid_tgid();
__u64 id = tracer_get_current_pid_tgid();
// Delete from go map. The value is not used after exiting this syscall.
// Keep value in openssl map.
bpf_map_delete_elem(&go_kernel_write_context, &id);
Expand Down
4 changes: 2 additions & 2 deletions bpf/go_uprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ static __always_inline int go_crypto_tls_get_fd_from_tcp_conn(struct pt_regs* ct
}

static __always_inline void go_crypto_tls_uprobe(struct pt_regs* ctx, struct bpf_map_def* go_context, enum ABI abi) {
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u64 pid_tgid = tracer_get_current_pid_tgid();
__u64 pid = pid_tgid >> 32;
if (!should_target(pid)) {
return;
Expand Down Expand Up @@ -251,7 +251,7 @@ static __always_inline void go_crypto_tls_uprobe(struct pt_regs* ctx, struct bpf
}

static __always_inline void go_crypto_tls_ex_uprobe(struct pt_regs* ctx, struct bpf_map_def* go_context, struct bpf_map_def* go_user_kernel_context, __u32 flags, enum ABI abi) {
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u64 pid_tgid = tracer_get_current_pid_tgid();
__u64 pid = pid_tgid >> 32;
if (!should_target(pid)) {
return;
Expand Down
28 changes: 28 additions & 0 deletions bpf/include/pids.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,34 @@ int _pid_in_map(struct bpf_map_def* pmap, __u32 pid) {
return shouldTargetGlobally != NULL && *shouldTargetGlobally == 1;
}

const volatile __u64 TRACER_NS_INO = 0;
#define TRACER_NAMESPACES_MAX 4
static __always_inline __u64 tracer_get_current_pid_tgid() {
unsigned int inum;

__u64 base_pid_tgid = bpf_get_current_pid_tgid();

if (TRACER_NS_INO == 0) {
return base_pid_tgid;
}

struct task_struct* task = (struct task_struct*)bpf_get_current_task();

int level = BPF_CORE_READ(task, group_leader, nsproxy, pid_ns_for_children, level);

for (int i = 0; i < TRACER_NAMESPACES_MAX; i++) {
if ((level - i) < 0) {
break;
}
inum = BPF_CORE_READ(task, group_leader, thread_pid, numbers[level - i].ns, ns.inum);
if (inum == TRACER_NS_INO) {
__u64 ret = BPF_CORE_READ(task, group_leader, thread_pid, numbers[level - i].nr);
ret = (ret << 32) | (base_pid_tgid & 0xFFFFFFFF);
return ret;
}
}
return base_pid_tgid;
}

int should_target(__u32 pid) {
return _pid_in_map(&target_pids_map, pid);
Expand Down
4 changes: 2 additions & 2 deletions bpf/openssl_uprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ static __always_inline int get_count_bytes(struct pt_regs* ctx, struct ssl_info*
static __always_inline void ssl_uprobe(struct pt_regs* ctx, void* ssl, void* buffer, int num, struct bpf_map_def* map_fd, size_t* count_ptr) {
long err;

__u64 id = bpf_get_current_pid_tgid();
__u64 id = tracer_get_current_pid_tgid();

if (!should_target(id >> 32)) {
return;
Expand All @@ -61,7 +61,7 @@ static __always_inline void ssl_uprobe(struct pt_regs* ctx, void* ssl, void* buf
}

static __always_inline void ssl_uretprobe(struct pt_regs* ctx, struct bpf_map_def* map_fd, __u32 flags) {
__u64 id = bpf_get_current_pid_tgid();
__u64 id = tracer_get_current_pid_tgid();

if (!should_target(id >> 32)) {
return;
Expand Down
6 changes: 3 additions & 3 deletions bpf/tcp_kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ static void __always_inline tcp_kprobes_forward_openssl(struct ssl_info* info_pt
static __always_inline void tcp_kprobe(struct pt_regs* ctx, struct bpf_map_def* map_fd_openssl, struct bpf_map_def* map_fd_go_kernel, struct bpf_map_def* map_fd_go_user_kernel) {
long err;

__u64 id = bpf_get_current_pid_tgid();
__u64 id = tracer_get_current_pid_tgid();

if (!should_watch(id >> 32)) {
return;
Expand Down Expand Up @@ -108,12 +108,12 @@ static __always_inline void tcp_kprobe(struct pt_regs* ctx, struct bpf_map_def*

SEC("kprobe/tcp_sendmsg")
void BPF_KPROBE(tcp_sendmsg) {
__u64 id = bpf_get_current_pid_tgid();
__u64 id = tracer_get_current_pid_tgid();
tcp_kprobe(ctx, &openssl_write_context, &go_kernel_write_context, &go_user_kernel_write_context);
}

SEC("kprobe/tcp_recvmsg")
void BPF_KPROBE(tcp_recvmsg) {
__u64 id = bpf_get_current_pid_tgid();
__u64 id = tracer_get_current_pid_tgid();
tcp_kprobe(ctx, &openssl_read_context, &go_kernel_read_context, &go_user_kernel_read_context);
}
43 changes: 27 additions & 16 deletions packet_sniffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,49 @@ import (
type packetFilter struct {
ingressFilterProgram *ebpf.Program
egressFilterProgram *ebpf.Program
ingressPullProgram *ebpf.Program
egressPullProgram *ebpf.Program
attachedPods map[string][2]link.Link
tcClient TcClient
}

func newPacketFilter(ingressFilterProgram, egressFilterProgram, pullIngress, pullEgress *ebpf.Program, pktsRingBuffer *ebpf.Map) (*packetFilter, error) {
var ifaces []int
links, err := netlink.LinkList()
if err != nil {
return nil, err
}
for _, link := range links {
ifaces = append(ifaces, link.Attrs().Index)
}

tcClient := &TcClientImpl{
TcPackage: &TcPackageImpl{},
}
for _, l := range ifaces {
if err := tcClient.SetupTC(l, pullIngress.FD(), pullEgress.FD()); err != nil {
return nil, err
}
}

pf := &packetFilter{
ingressFilterProgram: ingressFilterProgram,
egressFilterProgram: egressFilterProgram,
ingressPullProgram: pullIngress,
egressPullProgram: pullEgress,
attachedPods: make(map[string][2]link.Link),
tcClient: tcClient,
}
pf.update()
return pf, nil
}

func (p *packetFilter) update() {
var ifaces []int
links, err := netlink.LinkList()
if err != nil {
log.Error().Err(err).Msg("Get link list failed:")
return
}
for _, link := range links {
ifaces = append(ifaces, link.Attrs().Index)
}

for _, l := range ifaces {
if err := p.tcClient.SetupTC(l, p.ingressPullProgram.FD(), p.egressPullProgram.FD()); err != nil {
log.Error().Int("link", l).Err(err).Msg("Setup TC failed:")
continue
}
log.Info().Int("link", l).Msg("Attached TC programs:")
}
}

func (p *packetFilter) close() {
_ = p.tcClient.CleanTC()
for uuid := range p.attachedPods {
Expand All @@ -64,13 +75,13 @@ func (t *packetFilter) AttachPod(uuid, cgroupV2Path string) error {
return err
}
t.attachedPods[uuid] = [2]link.Link{lIngress, lEgress}
log.Info().Str("pod", uuid).Msg("Attaching pod:") //XXX
log.Info().Str("pod", uuid).Msg("Attaching pod:")

return nil
}

func (t *packetFilter) DetachPod(uuid string) error {
log.Info().Str("pod", uuid).Msg("Detaching pod:") //XXX
log.Info().Str("pod", uuid).Msg("Detaching pod:")
p, ok := t.attachedPods[uuid]
if !ok {
return fmt.Errorf("pod not attached")
Expand Down
3 changes: 3 additions & 0 deletions tls_process_discoverer.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ type podInfo struct {
var numberRegex = regexp.MustCompile("[0-9]+")

func (t *Tracer) updateTargets(addedWatchedPods []v1.Pod, removedWatchedPods []v1.Pod, addedTargetedPods []v1.Pod, removedTargetedPods []v1.Pod) error {
if t.packetFilter != nil {
t.packetFilter.update()
}
for _, pod := range removedTargetedPods {
if t.packetFilter != nil {
if err := t.packetFilter.DetachPod(string(pod.UID)); err == nil {
Expand Down
71 changes: 60 additions & 11 deletions tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package main
import (
"fmt"

"bytes"
"os"
"strconv"
"syscall"

Expand Down Expand Up @@ -50,6 +52,44 @@ type pidOffset struct {
offset uint64
}

type BpfObjectsImpl struct {
bpfObjs tracerObjects
specs *ebpf.CollectionSpec
}

func (objs *BpfObjectsImpl) loadBpfObjects(bpfConstants map[string]uint64) error {
var err error
opts := ebpf.CollectionOptions{
Programs: ebpf.ProgramOptions{
LogSize: ebpf.DefaultVerifierLogSize * 32,
},
}

reader := bytes.NewReader(_TracerBytes)
objs.specs, err = ebpf.LoadCollectionSpecFromReader(reader)
if err != nil {
return err
}

consts := make(map[string]interface{})
for k, v := range bpfConstants {
consts[k] = v
}
err = objs.specs.RewriteConstants(consts)
if err != nil {
return err
}

err = objs.specs.LoadAndAssign(&objs.bpfObjs, &opts)
if err != nil {
var ve *ebpf.VerifierError
if errors.As(err, &ve) {
log.Error().Msg(fmt.Sprintf("Got verifier error: %+v", ve))
}
}
return err
}

func (t *Tracer) Init(
chunksBufferSize int,
logBufferSize int,
Expand Down Expand Up @@ -81,25 +121,34 @@ func (t *Tracer) Init(

log.Info().Msg(fmt.Sprintf("Detected Linux kernel version: %s cgroups version: %v", kernelVersion, cgroupsVersion))

t.bpfObjects = tracerObjects{}
// TODO: cilium/ebpf does not support .kconfig Therefore; for now, we load object files according to kernel version.
if kernel.CompareKernelVersion(*kernelVersion, kernel.VersionInfo{Kernel: 4, Major: 6, Minor: 0}) < 1 {
t.bpfObjects = tracerObjects{}
if err := loadTracer46Objects(&t.bpfObjects, nil); err != nil {
return errors.Wrap(err, 0)
}
} else {
opts := ebpf.CollectionOptions{
Programs: ebpf.ProgramOptions{
LogSize: ebpf.DefaultVerifierLogSize * 32,
},
var hostProcIno uint64
fileInfo, err := os.Stat("/hostproc/1/ns/pid")
if err != nil {
// services like "apparmor" on EKS can reject access to system pid information
log.Warn().Err(err).Msg("Get host netns failed")
} else {
hostProcIno = fileInfo.Sys().(*syscall.Stat_t).Ino
log.Info().Uint64("ns", hostProcIno).Msg("Setting host ns")
}
if err := loadTracerObjects(&t.bpfObjects, &opts); err != nil {
var ve *ebpf.VerifierError
if errors.As(err, &ve) {
log.Error().Msg(fmt.Sprintf("Got verifier error: %+v", ve))
}
return errors.Wrap(err, 0)

objs := &BpfObjectsImpl{}

bpfConsts := map[string]uint64{
"TRACER_NS_INO": hostProcIno,
}
err = objs.loadBpfObjects(bpfConsts)
if err != nil {
log.Error().Msg(fmt.Sprintf("load bpf objects failed: %v", err))
return err
}
t.bpfObjects = objs.bpfObjs
}

t.syscallHooks = syscallHooks{}
Expand Down

0 comments on commit 40334a3

Please sign in to comment.