Files
ja4-platform/docs/services/ja4ebpf/NGINX_APACHE_GUIDE.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

11 KiB

Guide : Capture HTTP Nginx et Apache httpd via ja4ebpf

Vue d'ensemble

ja4ebpf peut capturer le trafic HTTP complet depuis deux serveurs web différents :

  • Nginx : via recvfrom() syscall (kretprobe sur __x64_sys_recvfrom)
  • Apache httpd : via apr_socket_recv() uprobe dans libapr-1.so.0

Statut de validation

Serveur Kernel Statut Méthode Headers capturés
nginx CentOS 8 (4.18) Validé kretprobe __x64_sys_recvfrom Tous (sans troncature)
nginx Rocky Linux 9 (5.14+) Validé kretprobe __x64_sys_recvfrom Tous (sans troncature)
nginx Rocky Linux 10 (6.12) Validé kretprobe __x64_sys_recvfrom Tous (sans troncature)
Apache httpd CentOS 8 (4.18) Validé uprobe apr_socket_recv Tous (sans troncature)
Apache httpd Rocky Linux 10 (6.12) Validé uprobe apr_socket_recv Tous (sans troncature)
Apache httpd Rocky Linux 9 (5.14+) Compatible uprobe apr_socket_recv Même méthode

Configuration

Configuration YAML

uprobes:
  enabled: true
  servers: ["nginx", "apache"]  # Active les deux serveurs

Variables d'environnement

JA4EBPF_UPROBES_ENABLED=true
JA4EBPF_UPROBES_SERVERS=nginx,apache

Architecture de capture

Nginx (kretprobe)

┌─────────────┐
│ nginx worker │─┐
└─────────────┘  │
                 ├─ recvfrom() ──┐
                 │                │
         ┌──────▼──────┐     │
         │ kretprobe   │     │
         │ __x64_sys   │     │
         │ recvfrom    │     │
         └─────────────┘     │
                              │
                      ┌───────▼──────┐
                      │ ja4ebpf     │
                      │ user space  │
                      └─────────────┘

Apache httpd (uprobe apr_socket_recv)

┌─────────────┐
│ httpd worker │─┐
└─────────────┘  │
                 ├─ apr_socket_recv() (libapr-1.so.0) ──┐
                 │                                    │
         ┌──────▼──────┐                         │
         │ uprobe      │                         │
         │ entry/return│                         │
         │ apr_socket  │                         │
         │ _recv       │                         │
         └─────────────┘                         │
                                               │
                                       ┌───────▼──────┐
                                       │ ja4ebpf     │
                                       │ user space  │
                                       └─────────────┘

Avantage Apache : L'uprobe sur apr_socket_recv capture directement au niveau application Apache Portable Runtime, ce qui la rend universelle sur tous les kernels 4.18+ (pas de dépendance aux tracepoints/kernel functions).

Déploiement multi-servers

Scénario 1 : ja4ebpf sur chaque serveur web

┌─────────────────┐     ┌─────────────────┐
│   rocky9 (nginx)  │     │   centos8 (apache)│
│                  │     │                  │
│  ┌────────────┐  │     │  ┌────────────┐  │
│  │  nginx     │  │     │  │  Apache     │  │
│  └─────┬──────┘  │     │  └─────┬──────┘  │
│        │          │     │        │          │
│  ┌─────▼──────┐  │     │  ┌─────▼──────┐  │
│  │  ja4ebpf   │  │     │  │  ja4ebpf   │  │
│  └────────────┘  │     │  └────────────┘  │
│                  │     │                  │
│  capture: recvfrom│     │  capture: apr_socket_recv
└──────────────────┘     └──────────────────┘

IP: 192.168.42.40       IP: 192.168.42.228

Configuration :

# rocky9
JA4EBPF_UPROBES_SERVERS=nginx

# centos8
JA4EBPF_UPROBES_SERVERS=apache

Scénario 2 : ja4ebpf sur machine tierce (recommandé)

┌─────────────────────────────────────────────────┐
│              analysis VM (ja4ebpf)              │
│                                                   │
│  ┌────────────┐  ┌─────────────┐                 │
│  │ nginx       │  │ Apache       │                 │
│  └──────┬─────┘  └──────┬──────┘                 │
│         │               │                          │
│         └───────┬───────┘                          │
│                 │                                    │
│         ┌───────▼──────────┐                       │
│         │     ja4ebpf      │                       │
│         │  (nginx/Apache) │                       │
│         └─────────────────┘                       │
└───────────────────────────────────────────────────┘

Configuration :

uprobes:
  enabled: true
  servers: ["nginx", "apache"]

Validation

Vérification nginx

# Vérifier que nginx capture
curl http://192.168.42.40/test -H "User-Agent: Test" -H "X-Request-ID: test-nginx-001"

# Logs ja4ebpf
tail -f /tmp/ja4ebpf-test.log | grep "\[nginx\]"
# Exemple: [nginx] HTTP: pid=116276 fd=8 GET /test (headers=5)

# ClickHouse
sudo docker exec analysis-clickhouse-1 clickhouse-client --query \
  "SELECT method, path, header_user_agent FROM ja4_logs.http_logs \
   WHERE time > now() - INTERVAL 1 MINUTE ORDER BY time DESC LIMIT 10"

Vérification Apache

# Vérifier que Apache capture
curl http://192.168.42.228/server-status -H "User-Agent: Test" -H "X-Request-ID: test-apache-001"

# Logs ja4ebpf
tail -f /tmp/ja4ebpf-apache.log | grep "\[apache\]"
# Exemple: [apache] HTTP: pid=71850 GET /server-status (data_len=420)

# ClickHouse
sudo docker exec analysis-clickhouse-1 clickhouse-client --query \
  "SELECT method, path, header_user_agent FROM ja4_logs.http_logs \
   WHERE time > now() - INTERVAL 1 MINUTE ORDER BY time DESC LIMIT 10"

Tests de validation

Test 1 : Headers complets

# Test nginx (20+ headers)
curl http://192.168.42.40/api/test \
  -H "User-Agent: Mozilla/5.0 (Validation-Agent)" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer token" \
  -H "X-Custom-1: value1" \
  -H "X-Custom-2: value2" \
  ... (jusqu'à 20 headers)

# Test Apache (20+ headers)
curl http://192.168.42.228/api/test \
  -H "User-Agent: Mozilla/5.0 (Validation-Agent)" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer token" \
  -H "X-Custom-1: value1" \
  -H "X-Custom-2: value2" \
  ... (jusqu'à 20 headers)

Test 2 : Path et query longs

# nginx
curl "http://192.168.42.40/api/v1/users/12345/profile/preferences?include=all&filter=active&sort=desc"

# Apache
curl "http://192.168.42.228/api/v1/users/12345/profile/preferences?include=all&filter=active&sort=desc"

Validation ClickHouse

-- Requête pour vérifier la capture
SELECT
    src_ip,
    method,
    path,
    query,
    substring(header_user_agent, 1, 40) as ua_preview,
    length(header_order_signature) as header_count,
    substring(header_order_signature, 1, 60) as headers_preview
FROM ja4_logs.http_logs
WHERE time > now() - INTERVAL 5 MINUTE
ORDER BY time DESC
LIMIT 20
FORMAT Pretty

Résultats de validation

Nginx (via recvfrom) - VALIDÉ sur Rocky Linux 9

  • Méthode HTTP capturée
  • Path complet sans troncature
  • Query string complète
  • Tous les headers capturés (y compris custom X-*)
  • User-Agent complet
  • Ordre des headers préservé
  • Données ClickHouse sans troncature

Exemple de capture validée :

SELECT method, path, host,
       length(header_order_signature) as headers_count,
       header_order_signature
FROM ja4_logs.http_logs
WHERE path = '/test-nginx-final'
-- Résultat : headers_count=6
-- header_order_signature: host;accept;user-agent;x-request-id;x-custom-1;x-custom-2

Apache httpd (via apr_socket_recv) - VALIDÉ

CentOS 8 (kernel 4.18) :

  • 2 événements HTTP capturés
  • Uprobe apr_socket_recv fonctionnel
  • libapr-1.so.0 détecté automatiquement

Rocky 10 (kernel 6.12) :

  • 1 événement HTTP capturé
  • Même méthode uprobe compatible
  • Universelle sur kernels 4.18+

Rapport complet : services/ja4ebpf/docs/APACHE_HTTP_VALIDATION.md

Dépannage

Apache ne capture pas

# Vérifier que Apache httpd utilise libapr
sudo lsof -p $(pgrep httpd | head -1) | grep libapr

# Vérifier que libapr-1.so.0 existe
ls -la /usr/lib64/libapr-1.so.0

# Vérifier les PIDs Apache
pgrep -a httpd

# Vérifier l'attachement uprobe
sudo bpftool prog show | grep apr_socket_recv

# Vérifier les logs ja4ebpf
tail -f /tmp/ja4ebpf-apache.log | grep -E "(\[uprobes\]|\[apache\])"

Nginx ne capture pas

# Vérifier les kretprobes attachés
sudo bpftool prog show | grep recvfrom

# Vérifier les PIDs nginx
pgrep -a nginx | wc -l

# Vérifier les logs ja4ebpf
tail -f /tmp/ja4ebpf-test.log | grep nginx

Fichiers BPF

uprobe_nginx.c

  • SEC("tp/syscalls/sys_enter_recvfrom") : Sauvegarde arguments recvfrom
  • SEC("kretprobe/__x64_sys_recvfrom") : Capture données + envoi vers pb_nginx_http

uprobe_apache.c

  • SEC("uprobe/apr_socket_recv") : Sauvegarde buf_ptr et len (entry)
  • SEC("uretprobe/apr_socket_recv") : Capture données + envoi vers pb_apache_http

Limitations

  1. Architecture nginx : Le kretprobe __x64_sys_recvfrom est spécifique à l'architecture x86_64
  2. Local : La capture doit se faire sur la même machine que le serveur web (pour accéder aux syscalls/fonctions)
  3. Performance : Chaque syscall/appel lu génère un événement BPF - le trafic très élevé peut impacter les performances
  4. Apache only RedHat : libapr-1.so.0 paths configurés pour RHEL/CentOS/Rocky/AlmaLinux uniquement

Références

  • Documentation complète : docs/services/ja4ebpf.md
  • Rapport validation Apache : services/ja4ebpf/docs/APACHE_HTTP_VALIDATION.md
  • Rapport validation nginx multi-kernel : services/ja4ebpf/docs/NGINX_MULTI_KERNEL_VALIDATION.md
  • Rapport validation nginx/ClickHouse : services/ja4ebpf/docs/CLICKHOUSE_VALIDATION_REPORT.md