Files
ja4-platform/docs/services/ja4ebpf.md
Jacquin Antoine 4a41e31822 feat(ebpf): Apache HTTP capture + nginx multi-kernel validation
**Apache HTTP capture via apr_socket_recv** :
- Uprobe sur libapr-1.so.0 (Apache Portable Runtime)
- Compatible tous kernels 4.18+ (CentOS 8, Rocky 9/10)
- Configuration unifiée : servers: ["nginx", "apache"]

**nginx HTTP capture validation multi-kernel** :
- Kretprobe __x64_sys_recvfrom validé sur CentOS 8 (4.18)
- Rocky 9 (5.14) et Rocky 10 (6.12) confirmés
- Contourne limitation tracepoint sys_exit_recvfrom

**Documentation** :
- docs/TEST_BUILD_STACK.md : stack complète test/build (VMs, Docker, RPMs)
- services/ja4ebpf/docs/APACHE_HTTP_VALIDATION.md : validation Apache
- services/ja4ebpf/docs/NGINX_MULTI_KERNEL_VALIDATION.md : validation nginx
- docs/architecture.md + docs/services/ja4ebpf.md mis à jour

**Tests unitaires Apache** :
- internal/loader/apache_test.go : tests libapr, paths, structures BPF
- internal/correlation/apache_test.go : tests corrélation HTTP Apache

**Packaging** :
- RPM spec mis à jour (version 0.3.0-1, changelog complet)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 19:49:40 +02:00

25 KiB

ja4ebpf — Agent eBPF CO-RE

Description

ja4ebpf est l'agent de collecte de données de la plateforme ja4-platform. C'est un binaire Go unique qui utilise eBPF CO-RE (Compile Once — Run Everywhere) pour observer passivement le trafic réseau d'un serveur Linux, sans modifier ni interrompre le serveur web cible (Apache, Nginx, Varnish, HAProxy, ou tout processus utilisant OpenSSL/BoringSSL).

Il capture simultanément les métadonnées réseau L3/L4 (TCP SYN), les paramètres TLS L5 (ClientHello), et le contenu applicatif L7 (requêtes HTTP déchiffrées), corrèle le tout en mémoire par la clé src_ip:src_port, et envoie les données vers ClickHouse en batch.

Architecture interne

  +-----------------+    TC ingress                +--------------------+
  |  réseau entrant  |---------------------------->|  bpf/tc_capture.c  |
  |                 |                             |                    |
  |  SYN packet     |  --> pb_tcp_syn  (perf)    |  Programme eBPF    |
  |  TLS ClientHello|  --> pb_tls_hello (perf)   |  CO-RE             |
  |  HTTP port 80   |  --> pb_http_plain (perf)  |                    |
  +-----------------+                             +--------------------+
                                                          |
  +-----------------+    uprobe SSL_read/SSL_write  +--------------------+
  |  serveur web    |--------------------------->  |  bpf/uprobe_ssl.c  |
  |  (OpenSSL)      |                             |                    |
  |  flux déchiffré |  --> pb_ssl_data  (perf)    |  Programme eBPF    |
  |  accept4 events |  --> pb_accept   (perf)    |  CO-RE             |
  +-----------------+                             +--------------------+
                                                          |
  +-----------------+    uprobe HTTP (nginx/Apache)  +-------------------+
  |  nginx recvfrom |  --> pb_nginx_http  (perf)   |  bpf/uprobe_nginx.c|
  |  Apache apr_recv|  --> pb_apache_http (perf)   |  bpf/uprobe_apache.c|
  +-----------------+                             +--------------------+
                                                          |
                                              +-----------v-----------+
                                              |     Go userspace       |
                                              |                        |
                                              |  internal/loader/      |
                                              |  - PerfEvent readers    |
                                              |  - 5 goroutines        |
                                              |                        |
                                              |  internal/parser/      |
                                              |  - JA4/JA3 calculator   |
                                              |  - H2ConnState (HPACK) |
                                              |  - HTTP/1.1 parser     |
                                              |                        |
                                              |  internal/dispatcher/  |
                                              |  - Magic Bytes router  |
                                              |                        |
                                              |  internal/correlation/ |
                                              |  - 256-shard manager   |
                                              |  - AcceptCache          |
                                              |  - GC 100ms            |
                                              |  - timeout 500ms       |
                                              |                        |
                                              |  internal/writer/      |
                                              |  - ClickHouse batch    |
                                              +----------+-------------+
                                                         |
                                              INSERT batch TCP :9000
                                                         |
                                                         v
                                              +----------------------+
                                              |  ja4_logs.           |
                                              |  http_logs_raw       |
                                              +----------------------+

Hooks eBPF

TC ingress — Couches L3/L4/L5

Le programme bpf/tc_capture.c est attaché aux interfaces réseau via TC (Traffic Control) en ingress (clsact qdisc). Par défaut, il s'attache sur toutes les interfaces UP (sauf loopback). Il s'exécute pour chaque paquet entrant :

Filtrage kernel-side :

  • Ports autorisés : la map allowed_ports (HASH) filtre par port destination/source. Seuls les ports configurés (défaut : 80, 443) sont traités.
  • IP/CIDR ignorés : la map ignored_src (LPM_TRIE) ignore le trafic provenant des CIDR configurés. Le lookup utilise le format {prefixlen, data[4]} en network byte order.

Paquets TCP SYN : extraction des options TCP et métadonnées IP depuis les headers du paquet.

  • bpf_skb_load_bytes() pour lire les options TCP depuis le skb
  • Émis via pb_tcp_syn (PerfEventArray)

ClientHello TLS : détection du type 0x16 (Handshake) et sous-type 0x01 (ClientHello).

  • bpf_skb_load_bytes() avec tailles en cascade (1024 → 512 → 256) pour capturer SNI et extensions
  • Le struct tls_hello_event place le payload à l'offset 0 (compatible kernel 4.18)
  • Émis via pb_tls_hello (PerfEventArray)

HTTP en clair (port 80/8080) : pour les connexions non chiffrées.

  • SYN/FIN/RST exclus (uniquement les segments porteurs de données)
  • bpf_skb_load_bytes() avec tailles en cascade (512 → 256 → 128 → 64)
  • Le struct http_plain_event place le payload à l'offset 0
  • Émis via pb_http_plain (PerfEventArray)

Uprobes SSL_read/SSL_write — Couche L7

Les uprobes s'attachent dynamiquement aux fonctions OpenSSL dans libssl.so :

Hook Type Rôle
SSL_set_fd uprobe Associe SSL*fd, peuple ssl_conn_map
SSL_read uprobe + uretprobe Capture les requêtes du client (direction=0)
SSL_write uprobe + uretprobe Capture les réponses du serveur (direction=1)

Tracepoints/Kretprobe HTTP serveurs web (Nginx, Apache)

Nginx HTTP en clair

Les hooks sys_enter_recvfrom / sys_exit_recvfrom capturent les appels système recvfrom() du serveur Nginx pour capturer le trafic HTTP en clair complet :

Hook Type État Rôle
tp_syscalls_sys_enter_recvfrom tracepoint Fonctionnel Sauvegarde les arguments recvfrom (sockfd, buf_ptr, len)
tp_sys_exit_recvfrom kretprobe Fonctionnel Capture les données lues + émet vers pb_ginx_http

Filtrage par PID nginx : La map nginx_pid_map ne permet que les processus nginx identifiés via /proc/<pid>/cmdline.

Apache httpd HTTP en clair

Les hooks uprobe_apr_socket_recv (entry) / uretprobe_apr_socket_recv (return) capturent les appels à la fonction apr_socket_recv d'Apache Portable Runtime pour capturer le trafic HTTP en clair complet :

Hook Type État Rôle
uprobe/apr_socket_recv uprobe Fonctionnel Sauvegarde buf_ptr et len depuis arguments
uretprobe/apr_socket_recv uretprobe Fonctionnel Capture les données lues + émet vers pb_apache_http

Cible : libapr-1.so.0 (Apache Portable Runtime) Chemin automatique : /usr/lib64/libapr-1.so.0 (RHEL/CentOS/Rocky/Alma 8/9/10)

Avantages :

  • Universelle : Fonctionne sur tous les kernels 4.18+ (pas de dépendance tracepoint)
  • Fiable : Capture directe au niveau application Apache
  • Performant : Un seul uprobe par processus Apache

Filtrage par PID Apache : La map apache_http_pid_map ne permet que les processus Apache httpd identifiés via /proc/<pid>/cmdline.

Corrélation fd → src_ip:src_port (3 niveaux de priorité) :

  1. ssl_conn_map[ssl_ptr] — si SSL_set_fd a été appelé et que fd_conn_map[fd] contient l'IP (via accept4)
  2. accept_map[{pid_tgid, fd}] — cache accept4 (tracepoint kernel)
  3. Fallback /proc/<pid>/net/tcp — lecture depuis l'espace utilisateur (moins fiable)

Les données sont émises via pb_ssl_data (PerfEventArray) avec un flag direction (0=client→serveur, 1=serveur→client).

Tracepoints accept4

Les tracepoints sys_enter_accept4 / sys_exit_accept4 (plus stables que les kprobes) capturent le tuple (src_ip, src_port, fd, pid_tgid) pour chaque nouvelle connexion acceptée. Ils peuplent :

  • accept_map[{pid_tgid, fd}] — pour corrélation SSL côté BPF
  • fd_conn_map[fd] — pour SSL_set_fd
  • pb_accept (PerfEventArray) — pour corrélation SSL côté Go (AcceptCache)

Maps eBPF résumé

Map Type Rôle
allowed_ports HASH (key=u16, val=u8) Ports TCP autorisés (peuplée depuis Go)
ignored_src LPM_TRIE (key={prefixlen, data[4]}, val=u8) CIDR/IP sources à ignorer (peuplée depuis Go)
tc_stats PERCPU_ARRAY (7 compteurs) Statistiques de debug BPF
ssl_conn_map HASH (key=ssl_ptr, val=ssl_conn_info) Association SSL* → fd + IP
fd_conn_map HASH (key=fd, val=ssl_conn_info) Association fd → IP (depuis accept4)
accept_map HASH (key={pid_tgid,fd}, val=accept_event) Cache accept4 côté BPF
ssl_args_map HASH (key=pid_tgid, val=ssl_read_args) Sauvegarde arguments SSL_read/Write entry
__tls_buf PERCPU_ARRAY (1 entrée) Buffer temp > 512o (stack eBPF limit)
__http_buf PERCPU_ARRAY (1 entrée) Buffer temp HTTP plain
__ssl_buf PERCPU_ARRAY (1 entrée) Buffer temp SSL data

Corrélation in-memory

Le gestionnaire internal/correlation maintient un état par connexion :

Mécanisme Valeur
Sharding 256 buckets (hash XOR src_ip ^ src_port)
Timeout orphelin 500 ms (→ session exportée vers ClickHouse)
Détection Slowloris 10 s (export partiel)
GC interval 100 ms
Keep-Alive len(Requests) incrémenté par requête

AcceptCache

Le AcceptCache map {tgid, fd} → SessionKey + dstIP + dstPort avec TTL 10s. Il est peuplé par les événements accept4 et consulté par le consommateur SSL quand ssl_conn_map a src_ip=0. Purge automatique toutes les 30s.

FDCache

Le FDCache résout fd → IP:port via /proc/<pid>/net/tcp (fallback quand accept4 n'est pas disponible). TTL 5s. Utilise parseHexIPv4 / parseHexIPv6 pour décoder le format little-endian du kernel.

Filtrage ignore_src (userspace)

En plus du filtrage BPF (LPM_TRIE côté kernel), les 5 goroutines de consommation appliquent un filtrage userspace via isIgnoredIP() + net.IPNet.Contains(). Cela couvre le chemin SSL (SSL_read/SSL_write/accept4) où le BPF LPM_TRIE ne peut pas filtrer (l'IP vient de ssl_conn_map ou /proc, pas du paquet réseau).

Routeur Magic Bytes (dispatcher)

Buffer reçu (SSL data ou HTTP plain)
    |
    +-- starts with "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" --> ProtoHTTP2
    |
    +-- starts with "GET " / "POST " / "PUT " / ...      --> ProtoHTTP1
    |
    +-- partial H2 preface prefix                         --> ProtoHTTP2 (buffer)
    |
    +-- autre                                              --> ProtoUnknown (ignoré)

Parsing HTTP/2 — H2ConnState

Le H2ConnState (package internal/parser) maintient un décodeur HPACK par-connexion avec table dynamique. Il traite les frames via golang.org/x/net/http2.Framer :

Frame Action
SETTINGS Extraction des 7 paramètres (+ paramètre 0x7 JA4H2). Mise à jour de la taille de table HPACK.
WINDOW_UPDATE (stream 0) Capture de l'incrément de fenêtre connexion
HEADERS + CONTINUATION Assemblage des fragments, décodage HPACK, extraction pseudo-headers ordre
DATA Comptage des octets par stream
PRIORITY Capture des paramètres de priorité (StreamDep, Exclusive, Weight)
RST_STREAM Transition du stream vers "closed"
GOAWAY Capture LastStreamID + ErrCode
PING Détection ACK

Fingerprinting Akamai :

  • h2_fingerprint : SETTINGS[pairs]|WINDOW_UPDATE[value]|PRIORITY[0/1]|PSEUDO_ORDER[order]
  • h2_settings_fp : liste brute des paramètres SETTINGS
  • h2_pseudo_order : notation abrégée (ex: m,a,s,p pour :method, :authority, :scheme, :path)

Données collectées

L3/L4 (TCP SYN)

Champ Description
src_ip, src_port Clé de corrélation
dst_ip, dst_port Destination IP et port
ttl Time To Live initial
df_bit Don't Fragment bit
ip_id IP Identification (0 = Linux/VPN/spoofé)
ip_total_length Longueur totale IP (octets)
window_size Taille fenêtre TCP SYN
window_scale Option Window Scale (RFC 1323), 0xFF = absent
mss Maximum Segment Size, 0 = absent
tcp_options Options TCP brutes (40 octets max), noms abrégés (MSS, WS, SACK, TS, NOP)

L5 (TLS ClientHello)

Champ Description
tls_version Version TLS la plus haute annoncée (extrait des SupportedVersions)
cipher_suites Liste suites cryptographiques (hex séparées par tirets)
extensions Liste IDs extensions TLS (hex séparés par tirets)
supported_groups Groupes Diffie-Hellman supportés
ec_point_formats Formats de points elliptiques
alpn ALPN list (h2, http/1.1, ...)
sni Server Name Indication
ja4 Empreinte JA4 (FoxIO)
ja3_raw Empreinte JA3 brute (version,ciphers,exts,groups,ecfmts)
ja3_hash JA3 hash MD5

L7 HTTP/1.1

Champ Description
method Méthode HTTP
path Chemin (sans query string)
query_string Paramètres query (sans le '?')
host En-tête Host ou TLS SNI
http_version HTTP/1.0 ou HTTP/1.1
headers_raw Noms d'en-têtes joints par ";"
header_order_signature Signature de l'ordre
status_code Code de statut de la réponse
response_size Taille réponse (octets)
duration_ms Durée requête
client_headers JSON des en-têtes capturés
header_User-Agent, etc. 16 en-têtes individuels capturés

L7 HTTP/2 (preface client)

Champ Description
h2_header_table_size SETTINGS ID 1 (-1 si absent du preface)
h2_enable_push SETTINGS ID 2
h2_max_concurrent_streams SETTINGS ID 3
h2_initial_window_size SETTINGS ID 4
h2_max_frame_size SETTINGS ID 5
h2_max_header_list_size SETTINGS ID 6
h2_enable_connect_protocol SETTINGS ID 8 (RFC 8441)
h2_window_update Incrément WINDOW_UPDATE connexion
h2_has_priority 1 si une frame PRIORITY a été reçue
h2_settings_ack 1 si SETTINGS ACK reçu
h2_pseudo_order Ordre pseudo-headers (ex: m,a,s,p)
h2_fingerprint Fingerprint composite Akamai
h2_settings_fp Chaîne brute des SETTINGS

eBPF CO-RE

Aspect Détail
Compilateur clang + llvm via bpf2go
Target bpf (amd64, kernel 4.18+)
BTF source /sys/kernel/btf/vmlinux (disponible RHEL 8+)
Relocations cilium/ebpf résout automatiquement les offsets struct
Embed go:generate génère ja4tc_x86_bpfel.go + ja4ssl_x86_bpfel.go
Compatibilité Rocky/RHEL Linux 8, 9, 10 (kernel 4.18+)
IPC BPF_MAP_TYPE_PERF_EVENT_ARRAY (kernel 4.4+)
Lecture paquets bpf_skb_load_bytes() (kernel 4.5+) avec tailles constantes en cascade

Contrainte kernel 4.18 : le vérificateur BPF rejette les tailles variables vers map values. Toutes les copies de payload utilisent des tailles constantes (1024 → 512 → 256 pour TLS, 512 → 256 → 128 → 64 pour HTTP). Les structs > 512o utilisent un PERCPU_ARRAY temporaire (limite stack eBPF).

Configuration

# /etc/ja4ebpf/config.yml

# Interfaces réseau à surveiller (TC ingress).
# "any" = toutes les interfaces UP (sauf loopback).
# Ou liste explicite : ["eth0", "eth1"]
interfaces:
  - any

# Chemin vers libssl pour les uprobes SSL_read/SSL_write/SSL_set_fd
ssl_lib_path: "/usr/lib64/libssl.so.3"

# Ports TCP à surveiller (filtrage BPF côté kernel)
listen_ports:
  - 80
  - 443

# CIDR/IP sources à ignorer (filtrage BPF LPM_TRIE + filtrage userspace SSL)
# ignore_src:
#   - 10.0.0.0/8
#   - 172.16.0.0/12
#   - 192.168.0.0/16
#   - 127.0.0.1

# Configuration des uprobes pour capture HTTP serveurs web
uprobes:
  enabled: true
  # Liste des serveurs à monitorer : "nginx", "apache", ou les deux
  servers:
    - nginx
    - apache
  # Nombre de tentatives d'attachement (processus peuvent démarrer après ja4ebpf)
  max_retries: 30
  retry_interval_sec: 2

# Mode debug
debug: false

clickhouse:
  dsn: "clickhouse://default:@127.0.0.1:9000/ja4_logs?async_insert=0"
  batch_size: 500
  flush_secs: 1

correlation:
  timeout_ms: 500
  slowloris_ms: 10000

log:
  level: "info"
  format: "json"

Variables d'environnement

Variable Défaut Description
JA4EBPF_CONFIG /etc/ja4ebpf/config.yml Chemin du fichier config
JA4EBPF_INTERFACES any Interfaces (séparées par virgule)
JA4EBPF_INTERFACE Rétrocompatibilité : une seule interface
JA4EBPF_SSL_LIB_PATH /usr/lib64/libssl.so.3 Chemin libssl
JA4EBPF_CLICKHOUSE_DSN voir config DSN ClickHouse
JA4EBPF_DEBUG false Mode debug (true, 1, yes)
JA4EBPF_LISTEN_PORTS 80,443 Ports surveillés (séparés par virgule)
JA4EBPF_IGNORE_SRC CIDR ignorés (séparés par virgule)

Build

# Build complet (bytecode eBPF + binaire Go) — Docker Rocky Linux
make build

# Build RPMs (multi-distro el8/el9/el10)
make rpm-ja4ebpf

# Tests unitaires (exécutés dans le conteneur de build)
make test

Note : La compilation eBPF nécessite clang/llvm et s'effectue dans un conteneur Docker Rocky Linux, pas sur le système hôte.

Structure du code

services/ja4ebpf/
├── bpf/
│   ├── bpf_types.h            # Structs C partagées + déclarations maps PerfEventArray
│   ├── headers/vmlinux.h      # Types kernel BTF (auto-généré)
│   ├── tc_capture.c           # Programme TC ingress (L3/L4/L5 + HTTP plain)
│   ├── uprobe_ssl.c           # Programme uprobes SSL + tracepoints accept4
│   ├── uprobe_nginx.c         # Programme uprobes nginx HTTP (recvfrom)
│   └── uprobe_apache.c        # Programme uprobes Apache HTTP (apr_socket_recv)
├── cmd/ja4ebpf/
│   ├── main.go                # Point d'entrée : 5 goroutines consumer + config
│   ├── apache_test.go         # Tests Apache (PID detection, libapr paths, corrélation)
│   └── main_test.go           # Tests parseCIDRs, parseIgnoreNets, isIgnoredIP, parseTCPOptions
├── internal/
│   ├── loader/
│   │   ├── loader.go          # Chargement eBPF + PerfEvent readers + attachement TC/uprobes
│   │   ├── ja4tc_x86_bpfel.go # Bytecode TC embarqué (généré par bpf2go)
│   │   ├── ja4ssl_x86_bpfel.go# Bytecode SSL embarqué (généré par bpf2go)
│   │   ├── ja4nginx_x86_bpfel.go# Bytecode nginx embarqué (généré par bpf2go)
│   │   └── ja4apache_x86_bpfel.go# Bytecode Apache embarqué (généré par bpf2go)
│   ├── parser/
│   │   ├── tls.go             # ParseClientHello + ComputeJA4 + ComputeJA3
│   │   ├── http1.go           # Parser HTTP/1.1 (requêtes + réponses)
│   │   ├── http2.go           # Constantes HTTP/2 + filtres en-têtes capturés
│   │   ├── h2conn.go          # H2ConnState : framer + HPACK + fingerprinting
│   │   ├── tls_test.go        # Tests JA4, JA3, SNI, extensions
│   │   ├── http1_test.go      # Tests HTTP/1.1
│   │   └── http2_test.go      # Tests H2ConnState complet
│   ├── dispatcher/
│   │   ├── dispatcher.go      # Routeur Magic Bytes (ProtoHTTP1/2/Unknown)
│   │   └── dispatcher_test.go # Tests Classify
│   ├── correlation/
│   │   ├── session.go          # Structs L3L4, TLSInfo, HTTPRequest, SessionState
│   │   ├── manager.go         # Gestionnaire sessions 256-shard + GC
│   │   ├── accept_cache.go    # AcceptCache {tgid,fd} → SessionKey (TTL 10s)
│   │   ├── correlation_test.go# Tests Manager + SessionState
│   │   └── accept_cache_test.go# Tests AcceptCache
│   ├── procutil/
│   │   ├── proc_lookup.go     # FDCache : résolution fd → IP via /proc/net/tcp
│   │   └── proc_lookup_test.go# Tests parseHexIPv4/IPv6, isIPv4MappedIPv6
│   └── writer/
│       ├── clickhouse.go      # Writer ClickHouse (batch + JSON → http_logs_raw)
│       └── clickhouse_test.go # Tests formatTCPOptions, H2 fingerprints, etc.
├── packaging/
│   ├── rpm/ja4ebpf.spec       # Spec RPM (el8/el9/el10)
│   └── systemd/ja4ebpf.service # Unit systemd
├── config.yml.example         # Exemple de configuration
├── Dockerfile                 # Image de production
├── Dockerfile.tests           # Image de tests
├── Dockerfile.package         # Build RPM multi-distro
└── Makefile

Problèmes connus

HTTP Apache via apr_socket_recv — VALIDÉ (2026-04-20)

Solution implémentée : Uprobe sur apr_socket_recv dans libapr-1.so.0 (Apache Portable Runtime).

Détails : Contrairement à nginx qui utilise recvfrom(), Apache event MPM utilise les fonctions APR pour les I/O réseau. L'uprobe sur apr_socket_recv capture les données HTTP directement au niveau application.

Validation :

  • CentOS 8 (kernel 4.18) : 2 événements HTTP capturés
  • Rocky 10 (kernel 6.12) : 1 événement HTTP capturé
  • Universelle sur kernels 4.18+ (pas de dépendance tracepoint)
  • Rapport de validation : services/ja4ebpf/docs/APACHE_HTTP_VALIDATION.md

HTTP Nginx via recvfrom — VALIDÉ multi-kernels (2026-04-20)

Solution implémentée : Kretprobe sur __x64_sys_recvfrom.

Validation :

  • CentOS 8 (kernel 4.18) : kretprobe attaché (prog 835)
  • Rocky 9 (kernel 5.14) : capture HTTP complète validée
  • Rocky 10 (kernel 6.12) : kretprobe attaché (prog 909)
  • Universelle sur kernels 4.18+ (x86_64)
  • Rapport de validation : services/ja4ebpf/docs/NGINX_MULTI_KERNEL_VALIDATION.md

Solution implémentée : Remplacement du tracepoint sys_exit_recvfrom par un kretprobe sur __x64_sys_recvfrom.

Détails : Le tracepoint exit échouait avec "permission denied" sur Rocky Linux 9+. Le kretprobe contourne cette limitation en s'attachant directement à la fonction kernel.

Validation :

  • Toutes les données HTTP capturées sans troncature (path jusqu'à 39 chars, query jusqu'à 244 chars)
  • Headers custom (X-Request-ID, X-Custom-Header) complets
  • Tests unitaires Go ajoutés et validés
  • Rapport de validation : services/ja4ebpf/docs/CLICKHOUSE_VALIDATION_REPORT.md

Maps eBPF résumé

Map Type Rôle
allowed_ports HASH (key=u16, val=u8) Ports TCP autorisés (peuplée depuis Go)
ignored_src LPM_TRIE (key={prefixlen, data[4]}, val=u8) CIDR/IP sources à ignorer (peuplée depuis Go)
tc_stats PERCPU_ARRAY (7 compteurs) Statistiques de debug BPF
ssl_conn_map HASH (key=ssl_ptr, val=ssl_conn_info) Association SSL* → fd + IP
fd_conn_map HASH (key=fd, val=ssl_conn_info) Association fd → IP (depuis accept4)
accept_map HASH (key={pid_tgid,fd}, val=accept_event) Cache accept4 côté BPF
ssl_args_map HASH (key=pid_tgid, val=ssl_read_args) Sauvegarde arguments SSL_read/Write entry
nginx_pid_map HASH (key=u32, val=u8) Filtrage recvfrom par PID nginx
nginx_read_args_map HASH (key=pid_tgid, val=nginx_read_args) Sauvegarde arguments recvfrom entry
apache_http_pid_map HASH (key=u32, val=u8) Filtrage apr_socket_recv par PID Apache
apr_socket_recv_args_map HASH (key=pid_tgid, val=apr_socket_recv_args) Sauvegarde arguments apr_socket_recv entry
__tls_buf PERCPU_ARRAY (1 entrée) Buffer temp > 512o (stack eBPF limit)
__http_buf PERCPU_ARRAY (1 entrée) Buffer temp HTTP plain
__ssl_buf PERCPU_ARRAY (1 entrée) Buffer temp SSL data
__nginx_buf PERCPU_ARRAY (1 entrée) Buffer temp nginx HTTP
__apache_buf PERCPU_ARRAY (1 entrée) Buffer temp Apache HTTP

L'agent tourne sous l'utilisateur ja4ebpf (UID/GID 490 fixe). Les capabilities Linux accordées via AmbientCapabilities :

Capability Raison
CAP_BPF Chargement des programmes eBPF (kernel 5.8+)
CAP_SYS_ADMIN Uprobes + RHEL 8 (kernel 4.18, pré-CAP_BPF)
CAP_NET_ADMIN Attachement hooks TC ingress
CAP_NET_RAW Accès raw socket (fallback)
CAP_PERFMON Accès perf events pour les uprobes
CAP_SYS_PTRACE Résolution des offsets de fonctions pour les uprobes
CAP_DAC_READ_SEARCH Lecture /proc/<pid>/maps pour localiser libssl.so

LimitMEMLOCK=infinity est requis pour le mlock() des maps eBPF.