Skip to content

TLS Capture Architecture ​

Relevant source files

The following files were used as context for generating this wiki page:

Purpose and Scope ​

This document details the internal architecture of eCapture's core TLS capture subsystem (tls, gotls, and gnutls). It explains how eBPF programs are attached to user-space libraries and runtimes, how libraries and symbols are resolved across different versions of OpenSSL, BoringSSL, Go, and GnuTLS, the binary ABI contract between kernel C structs and user-space Go decoders, and the execution flows governing text, keylog, and pcapng capture modes.

Sources: AGENTS.md:74-94, docs/agent/architecture.md:37-49


eBPF Attachments and Probe Lifecycle ​

The TLS capture engine relies on github.com/gojue/ebpfmanager to manage the lifecycle of eBPF programs loaded into the kernel. Probes leverage uprobe and uretprobe attachments to intercept function entry and exit points in target shared libraries or compiled binaries. For pcapng and packet filtering modes, traffic control (TC) egress/ingress hooks are also attached.

Figure 1: Initialization Flow from CLI Entry to eBPF Manager Setup.

Sources: AGENTS.md:74-94, internal/probe/gnutls/gnutls_probe.go:57-117

The core probe lifecycle follows a strict initialization and attachment sequence:

  1. Configuration Validation: The probe configuration is validated idempotently docs/agent/architecture.md:99-102.
  2. Bytecode Selection: BaseProbe.SelectBytecode selects the appropriate CO-RE or non-CO-RE object file based on kernel BTF availability docs/agent/probe-development.md:105-109.
  3. Manager Initialization: manager.Manager initializes maps and programs using embedded binary assets internal/probe/gnutls/gnutls_probe.go:95-100.
  4. Attachment: bpfManager.Start() attaches uprobes to target symbols (e.g., SSL_write, SSL_read, gocryptotls_*, gnutls_record_send) internal/probe/gnutls/gnutls_probe.go:101-103, internal/probe/gnutls/gnutls_probe.go:210-212.
  5. Event Loop: Perf or ring-buffer event readers stream binary structures from kernel maps to user-space handlers AGENTS.md:76-88, internal/probe/gnutls/gnutls_probe.go:108-114.

Sources: AGENTS.md:74-94, docs/agent/architecture.md:125-144, internal/probe/gnutls/gnutls_probe.go:57-117


Library and Symbol Resolution ​

Each TLS module targets different runtime environments with distinct symbol and data structure layouts:

OpenSSL and BoringSSL (tls) ​

Go Native TLS (gotls) ​

  • Targets Go binaries by hooking internal crypto/tls functions (conn.Read, conn.Write) or standard library TCP/netFD bindings docs/agent/architecture.md:42.
  • Resolves Go's calling conventions dynamically, supporting both stack-based ABIs (Go < 1.17) and register-based ABIs (Go >= 1.17) via helper macros in kern/go_argument.h kern/go_argument.h:18-128.
  • Extracts file descriptors and IP tuples (src_ip, dst_ip, ports) directly from Go's internal net.netFD and net.TCPAddr runtime structures kern/gotls_kern.c:110-187.

GnuTLS (gnutls) ​

Sources: docs/agent/architecture.md:39-49, kern/go_argument.h:18-128, kern/openssl_masterkey.h:19-40, kern/openssl_masterkey_3.0.h:22-25, kern/openssl_masterkey_3.2.h:26-29


C/Go Event ABI and Binary Serialization ​

Data exchanged between kernel eBPF programs and user-space Go code constitutes a strict binary ABI. Any alteration to field widths, sign, or padding breaks decoding.

Figure 2: Binary Event ABI Pipeline from Kernel C Struct to User-Space Go Decoders.

Sources: AGENTS.md:98-115, kern/gotls_kern.c:33-50, internal/probe/gotls/event.go:34-145

Example: GoTLS Event ABI Mapping ​

The kernel structure struct go_tls_event kern/gotls_kern.c:33-50 is mapped directly into GoTLSDataEvent internal/probe/gotls/event.go:34-74 in Go. Fields are deserialized sequentially using binary.Read with explicit little-endian byte ordering internal/probe/gotls/event.go:77-127:

C Field NameC TypeGo Field NameGo TypeOffset / Size
ts_nsu64Timestampuint640 / 8 bytes kern/gotls_kern.c:34, internal/probe/gotls/event.go:38
pidu32Piduint328 / 4 bytes kern/gotls_kern.c:35, internal/probe/gotls/event.go:39
tidu32Tiduint3212 / 4 bytes kern/gotls_kern.c:36, internal/probe/gotls/event.go:40
data_lens32DataLenint3216 / 4 bytes kern/gotls_kern.c:37, internal/probe/gotls/event.go:41
event_typeu8EventTypeuint820 / 1 byte kern/gotls_kern.c:38, internal/probe/gotls/event.go:42
padu8[3]pad[3]byte21 / 3 bytes kern/gotls_kern.c:39, internal/probe/gotls/event.go:43
fdu32Fduint3224 / 4 bytes kern/gotls_kern.c:40, internal/probe/gotls/event.go:44
src_ipu8[16]SrcIP[16]byte28 / 16 bytes kern/gotls_kern.c:41, internal/probe/gotls/event.go:45
datachar[]Data[]byte84 / Variable kern/gotls_kern.c:49, internal/probe/gotls/event.go:53

Sources: AGENTS.md:98-115, kern/gotls_kern.c:33-50, internal/probe/gotls/event.go:34-145


Shared and Module-Specific Execution Flows ​

All three core TLS modules (tls, gotls, and gnutls) share a common execution pipeline after events reach user space:

  1. Readers & Decoders: Perf buffer readers invoke DecodeFromBytes on incoming raw byte slices, validating lengths and bounds against malicious or malformed inputs internal/probe/gotls/event.go:77-145.
  2. Synchronous Dispatcher: Decoded events are passed to events.Dispatcher, which fans out synchronously and unordered to all registered output handlers docs/agent/architecture.md:20-24, docs/agent/probe-development.md:125-128.
  3. Capture Mode Handlers:

Sources: AGENTS.md:74-94, docs/agent/architecture.md:20-24, docs/agent/probe-development.md:125-128, kern/openssl.h:23-39, kern/openssl_masterkey.h:35-105, internal/probe/gnutls/gnutls_probe.go:180-186

TLS Capture Architecture has loaded
Download