145 Commits

Author SHA1 Message Date
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
4d30d9a7cb feat(ebpf): Apache HTTP capture implementation (WIP on Rocky 10)
- Implemented Apache HTTP capture using recvfrom syscall (model identical to nginx)
- Added sys_enter_recvfrom + kretprobe __x64_sys_recvfrom approach
- Renamed Apache BPF maps (apache_http_pid_map, apache_http_recv_args_map) to avoid conflicts with nginx
- Added support for recvfrom and recvmsg syscalls (recvmsg support incomplete)

Test results:
- Rocky 9 (kernel 5.14): nginx HTTP capture works perfectly with full headers
- Rocky 10 (kernel 6.12): Apache HTTP capture NOT working (headers=0)
- CentOS 8 (kernel 4.18): Apache HTTP capture NOT working (headers=0)

Root cause: Apache event MPM uses async epoll model that doesn't trigger
recvfrom syscalls the same way as nginx. Further investigation needed
for Apache-specific capture methods.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 18:22:10 +02:00
8d817414b3 fix(ebpf): rename Apache BPF maps to avoid conflicts with nginx
- Rename apache_pid_map to apache_http_pid_map
- Rename apache_read_args_map to apache_http_recv_args_map
- Update all references in C code and Go loader
- Attempt both tracepoints and kretprobe for Apache HTTP capture

Test results:
- Rocky 9 (kernel 5.14): nginx HTTP capture works perfectly
- Rocky 10 (kernel 6.12): Apache HTTP capture not working (headers=0)
- CentOS 8 (kernel 4.18): Apache HTTP capture not working

The issue appears to be that Apache event MPM may not use recvfrom()
in the same way as nginx, or uses a different code path.

Further investigation needed for Apache HTTP capture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 16:05:54 +02:00
a2e0cfa2f3 feat(ebpf): add Apache httpd HTTP capture via kretprobe recvfrom
- Add uprobe_apache.c with kretprobe on __x64_sys_recvfrom for Apache HTTP capture
- Update loader.go to support unified "servers" configuration instead of separate nginx_bin_path/apache_enabled
- Add consumeApacheHTTPEvents() function to process Apache HTTP events
- Update bpf_types.h to add Apache-specific BPF maps and structs
- Fix perf event array value_size for pb_apache_http (must be sizeof(__u32) not struct size)
- Add NGINX_APACHE_GUIDE.md documentation for HTTP capture from both servers

Validation results:
- nginx HTTP capture:  Working (57 headers captured, no truncation)
- Apache HTTP capture: ⚠️ Under investigation (kretprobe not triggering on CentOS 8 kernel 4.18)

Configuration:
- JA4EBPF_UPROBES_ENABLED=true
- JA4EBPF_UPROBES_SERVERS=nginx,apache (or "both")

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 14:11:56 +02:00
cba1cca180 refactor(ebpf): simplify web server configuration with server list
Refactor uprobes configuration to use a single server list instead
of separate nginx_bin_path and apache_enabled options.

Configuration changes:
- Uprobes.Servers: []string (was: NginxBinPath + ApacheEnabled)
  - Accepts: ["nginx"], ["apache"], or ["nginx", "apache"]
  - Can also use "both" to enable both servers
- Environment variable: JA4EBPF_UPROBES_SERVERS (was: separate vars)

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

  Environment:
    JA4EBPF_UPROBES_SERVERS=nginx,apache

Code changes:
- Generic loop over cfg.Uprobes.Servers for attachment and consumption
- Remove duplicate checks for Enabled/ApacheEnabled
- Update attachNginxUprobesWithRetry to use default nginx path
- Update attachApacheUprobesWithRetry to remove ApacheEnabled check
- Update documentation to reflect both nginx and apache support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 13:43:32 +02:00
7dfe640003 feat(ebpf): add Apache httpd HTTP capture via read() syscall
Add support for capturing HTTP traffic from Apache httpd using
tracepoint/kretprobe on read() syscall.

Changes:
- bpf/uprobe_apache.c: New BPF program for Apache httpd capture
  - Uses tp/syscalls/sys_enter_read to save arguments
  - Uses kretprobe/__x64_sys_read to capture data (avoids tracepoint exit issues)
- bpf/bpf_types.h: Add Apache-specific structures and maps
  - struct apache_http_event (same structure as nginx_http_event)
  - struct read_args (shared between enter/exit)
  - apache_pid_map for filtering by PID
  - apache_read_args_map for argument storage
  - pb_apache_http perf buffer
- internal/loader/loader.go: Add Apache support
  - Add Ja4ApacheObjects, apachePidMap, ApacheHTTPReader
  - Add go:generate directive for uprobe_apache.c
  - Add AttachUprobesApache(), AddApachePid(), RemoveApachePid()
  - Add findApachePIDs() to discover Apache httpd processes
- cmd/ja4ebpf/main.go: Add Apache runtime support
  - Add ApacheEnabled config option
  - Add attachApacheUprobesWithRetry() with automatic retry
  - Add consumeApacheHTTPEvents() to process Apache HTTP events
  - Add apache counter to eventCounters
  - Update debugStatsDumper to show apache events

Configuration:
- Enable with: uprobes.apache_enabled=true or JA4EBPF_APACHE_ENABLED=1
- Automatically discovers httpd/apache2 processes via /proc/[pid]/cmdline

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 13:38:58 +02:00
382683710a feat(ebpf): add nginx HTTP capture infrastructure via kretprobe recvfrom
Add supporting infrastructure for nginx HTTP capture using kretprobe
on __x64_sys_recvfrom to replace the blocked tracepoint sys_exit_recvfrom.

Changes:
- bpf/bpf_types.h: Add nginx_pid_map for filtering recvfrom by PID
- cmd/ja4ebpf/main.go: Add Uprobes configuration section
- Makefile: Add test targets for recvfrom validation
- internal/loader: Generate nginx HTTP event structures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 13:30:41 +02:00
bb2160efbc docs: update documentation for kretprobe recvfrom fix
Update all documentation to reflect the resolved HTTP nginx capture
issue via kretprobe on __x64_sys_recvfrom.

Changes:
- README.md: Update HTTP status table showing kretprobe is now working
- docs/services/ja4ebpf.md: Replace tracepoint with kretprobe in hooks table,
  mark issue as resolved with validation reference
- docs/architecture.md: Clarify TC HTTP plain capture is packet-level only

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 13:30:02 +02:00
3e00e7bc7b fix(ebpf): replace tracepoint with kretprobe for sys_exit_recvfrom
Fixes "permission denied" error when attaching tracepoint sys_exit_recvfrom
on Rocky Linux 9 (kernel 5.14+). The tracepoint exit has stricter permissions
than entry tracepoints.

Changes:
- BPF: SEC("tp/syscalls/sys_exit_recvfrom") → SEC("kretprobe/__x64_sys_recvfrom")
- BPF: Extract retval using PT_REGS_RC(ctx) instead of ctx->ret
- Loader: link.Tracepoint() → link.Kretprobe()
- Add nginxPidMap for filtering recvfrom calls by nginx PID

Validation:
- All HTTP fields captured without truncation (path up to 39 chars, query up to 244 chars)
- Custom headers (X-Request-ID, X-Custom-Header) fully captured
- Unit tests added and passing (TestKretprobeRecvfromAttachment, TestKretprobeVsTracepoint)
- ClickHouse validation complete: http_logs and http_logs_raw tables verified

Tested on:
- Rocky Linux 9 (kernel 5.14+)
- bpftool shows: kprobe name tp_sys_exit_recvfrom (kretprobe active)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 13:29:01 +02:00
9e4bfe8289 feat(ebpf): add nginx uprobes skeleton for HTTP L7 capture
Add initial implementation of nginx uprobes to capture complete HTTP
headers at application layer. This addresses the limitation of TC-based
capture which truncates headers spanning multiple packets.

Changes:
- Add uprobe_nginx.c with read() syscall interception
- Add nginx_read_args map for uretprobe correlation
- Add AttachUprobesNginx() method with retry support
- Config via uprobes.enabled in YAML or JA4EBPF_UPROBES_ENABLED env var

Current status:
-  HTTPS (TLS) capture works perfectly - complete headers via SSL_read
-  HTTP plain nginx uprobes don't fire - nginx uses recv() not read()
- ⚠️  HTTP plain TC capture truncates headers (fundamental limitation)

Note: The nginx uprobes approach has limitations:
1. nginx uses recv()/recvmsg() syscalls, not read()
2. PLT attachment to glibc recv() doesn't trigger properly
3. Consider kprobes on sys_recvfrom or packet reassembly for future

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 01:29:53 +02:00
b6735b3081 fix(ebpf): fix SSL data capture bug at 4096-byte boundary
Fixed off-by-one error in uprobe_ssl.c where bpf_probe_read_user
was called with `data_len & (MAX_SSL_DATA - 1)` mask, causing
0-byte read when data_len was exactly 4096 (4096 & 4095 = 0).

This caused HTTP headers to be truncated when SSL_read returned
exactly 4096 bytes, resulting in host header values like "p"
instead of "platform".

The fix removes the incorrect bitwise operation and uses data_len
directly since it's already limited to MAX_SSL_DATA.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-19 15:42:24 +02:00
742f4420c0 fix(test): add ClickHouse readiness check before starting ja4ebpf
Fixed race condition where ja4ebpf would fail to connect to
ClickHouse at startup because ClickHouse HTTP port wasn't ready yet,
even though Docker healthcheck passed.

Changes:
- Add 30s wait loop with ClickHouse /ping endpoint check
- Log success message when ClickHouse is ready
- Applied to all 4 stacks: nginx, apache, nginx-varnish, hitch-varnish

Test results after fix:
- nginx: 240 rows, 175 JA4 fingerprints 
- apache: 257 rows, 191 JA4 fingerprints 
- nginx-varnish: 298 rows, 242 JA4 fingerprints 
- hitch-varnish: 247 rows, 177 JA4 fingerprints 

All L3/L4 metadata (TTL, MSS, Window), TLS fingerprinting (JA4, SNI),
and HTTP layer data are correctly captured and persisted.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-19 15:11:22 +02:00
506d151832 refactor(writer): improve headerVal function clarity
Changed from zero-value check to existence check for clearer intent.
Both approaches have similar performance characteristics for map lookups,
but using 'ok' makes it explicit we're checking for key presence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-19 14:38:56 +02:00
3429c74e03 feat(main): add error logging for HTTP parsing failures
Added warning logs when HTTP/1.x request/response parsing fails
to aid in debugging malformed traffic and parser issues.

Changes:
- Log warning when ParseHTTP1Response returns nil
- Log warning when ParseHTTP1Request returns nil (SSL events)
- Log warning when ParseHTTP1Request returns nil (plain HTTP)

Includes src IP and data length for context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-19 14:38:20 +02:00
678aa48a12 fix(correlation): prevent race condition in session manager
Fixed potential use-after-free in Update() when gcRound deletes
a session between GetOrCreate() and acquiring the session lock.

Changes:
- Add 'deleted' flag to SessionState
- Mark sessions as deleted before removing from map in gcRound
- Check deleted flag in Update and recreate session if needed

This ensures updates to deleted sessions create a new session
instead of modifying a freed/dangling reference.

Race detector verified: go test ./internal/correlation/... -race

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-19 14:34:30 +02:00
3353b3ae82 fix(main): prevent goroutine leak on shutdown
The ReadyCh consumer goroutine could block indefinitely during shutdown
because it only checked for channel closure but not context cancellation.

Changes:
- Add select statement to check both ctx.Done() and ReadyCh closure
- Add shutdown logging for better debugging
- Add 100ms grace period for goroutines to exit

Fixes potential hangs when receiving SIGTERM/SIGINT.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-19 14:24:50 +02:00
8beed18eea refactor(vm): reduce VM resource sizes for lighter test environment
Endpoints: 4 CPUs/4Go → 2 CPUs/2Go, Analysis: 4 CPUs/12Go → 2 CPUs/8Go

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 00:25:38 +02:00
36b5065a0a feat(e2e): add multi-IP endpoint architecture with dedicated traffic VM
Replace single-service-per-endpoint with all-ips mode running nginx, apache,
and hitch+varnish simultaneously on 3 dedicated IPs per VM (eth1 alias IPs).
Add a dedicated traffic VM with curl-impersonate for realistic TLS fingerprints,
parallelized traffic generation, and paired SNI_HOSTS/TARGET_IPS lists for
per-VM per-service hostname identification (e.g. rocky9-nginx-platform.test).

Key changes:
- run-tests-vm.sh: add setup_all_ips(), IP-specific Listen/bind directives
  with reset-before-apply pattern, graceful service availability checks
- run-e2e-test.sh: traffic VM architecture, all-ips mode, eth1 network,
  paired IP/SNI lists, updated cleanup for alias IPs
- generate-traffic.sh: parallel background jobs, curl-impersonate detection,
  auto source interface detection via ip route get, Host header in HTTP traffic
- Vagrantfile: add traffic VM with provision-traffic.sh
- provision-traffic.sh: install curl-impersonate and httpx for traffic gen
- test-rpm.sh: multi-interface TC check, updated ja4ebpf config
- clickhouse-init.sh: load CSV stubs for Anubis/bot-networks dictionaries
- Remove obsolete correlator/sentinel/mod-reqin-log docs
- Add h2_settings_ack column to http_logs schema
- Upgrade Go toolchain to 1.25.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 14:25:24 +02:00
f0c8fe81c6 feat(ja4ebpf): add multi-interface TC, LPM_TRIE ignore_src, unit tests, and fix bugs
- Add multi-interface TC attachment (default "any" = all UP interfaces)
- Add BPF LPM_TRIE map ignored_src for kernel-side CIDR filtering
- Add userspace ignore_src filtering for SSL/accept4 path via net.IPNet.Contains()
- Add AcceptCache for fd→SessionKey correlation with TTL and Close()
- Add 5 test files covering writer, procutil, dispatcher, accept_cache, and cmd
- Fix formatTCPOptions infinite loop on EOL (case 0 break→return)
- Fix pseudoOrderToShort panic on empty slice (negative cap)
- Fix AcceptCache goroutine leak (add done channel + Close())
- Update config.yml.example with interfaces, listen_ports, ignore_src
- Rewrite docs/services/ja4ebpf.md (was massively stale: XDP, RingBuffer, etc.)
- Fix stale XDP/RingBuffer references in docs/architecture.md, thesis, tls.go

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 01:49:26 +02:00
fd84aebc44 fix(ja4ebpf): correct HPACK static table per RFC 7541 and decode indexed representations
The HPACK static table was completely wrong from index 15 onwards — entries
were shifted and missing, causing all header name lookups to return wrong
names (e.g. index 19 returned "cookie" instead of "accept"). Rewrite the
entire table as hpackStaticEntry{Name,Value} structs matching RFC 7541 Appendix
A (indices 1-61) plus browser extensions (62-100). Fix DecodeH2HeadersBlock to
properly decode fully-indexed representations (6.1) which were silently dropped
before — now both name and value are extracted from the static table entry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 15:24:04 +02:00
0975d40609 feat(ja4ebpf): add dst_ip/dst_port to TLS and HTTP plain events for complete L3/L4
Add dst_ip and dst_port fields to tls_hello_event BPF struct and populate
them in tc_capture.c. Update Go TLS event handler with new byte offsets
(payload[2048]+src_ip(4)+dst_ip(4)+src_port(2)+dst_port(2)+payload_len(2)+
timestamp_ns(8) = 2070 bytes). Read dst_ip/dst_port from HTTP plain events
and use them to populate L3L4 when SYN was not captured, ensuring dst_ip
and dst_port are always available in ClickHouse for both TLS and HTTP sessions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 14:31:46 +02:00
65d833bb18 fix(ja4ebpf): correct BPF struct byte offsets and regenerate SSL_write programs
Fix two critical offset bugs introduced when ip_total_length was added to
tcp_syn_event: tcp_options_raw offset 21→23 and tcp_options_len offset 61→63,
plus minimum size check 70→72. Fix ssl_data_event direction field offset from
4118 (inside timestamp_ns) to 4126. Simplify attachSSLWrite to use generated
objects directly instead of dynamic spec loading. Regenerate BPF objects with
SSL_write uprobe programs included.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 14:06:28 +02:00
24306ef390 feat(ja4ebpf): add SSL_write uprobe, HPACK decoder, and AcceptCache for session correlation
Add uprobe_ssl_write_entry/uretprobe_ssl_write_exit to capture server HTTP
responses via SSL_write with direction=1. Implement full HPACK decoder
(RFC 7541 static table, multi-byte integers, literal representations) for
HTTP/2 header extraction. Add AcceptCache mapping {tgid,fd}→SessionKey
from accept4 events as authoritative source for SSL correlation when BPF
ssl_conn_map has src_ip=0. Add ip_total_length to tcp_syn_event BPF struct.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 03:34:43 +02:00
a02423fd18 feat: maximize data completeness across L3/L4/TLS/HTTP layers and add E2E test infra
Add SSL_write uprobe for HTTP response capture, HPACK decoder for HTTP/2
header extraction, and AcceptCache for reliable SSL/TC session correlation.
Populate all ClickHouse fields including tcp_meta_options, ip_meta_total_length,
syn_to_clienthello_ms, client_headers, TLS cipher suites/extensions, and
h2_enable_connect_protocol. Increase BPF capture buffers (HTTP 512B, TLS 1024B).
Add distributed E2E testing infrastructure with multi-VM Vagrant setup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 03:34:33 +02:00
e25caa85da fix(ja4ebpf): remove double bswap16 on accept4 port
The manual byte assembly (sa_buf[2]<<8 | sa_buf[3]) already produces
a host-byte-order port value; __builtin_bswap16 was swapping it again,
causing SSL events to use wrong source ports and preventing TLS/HTTP
session correlation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 02:57:29 +02:00
61addc8cfa feat: JA3 fingerprinting, SSL correlation fix, ML pipeline overhaul, E2E test infra
ja4ebpf:
- Add JA3 raw + MD5 hash fingerprinting (ComputeJA3 in TLS parser)
- Fix accept4 port double-swap bug (__builtin_bswap16 on already-host-order value)
- Fix scheme override bug in ClickHouse writer (HTTP block clearing HTTPS)
- Add HTTP/2 passive fingerprinting (Akamai H2 FP, SETTINGS, pseudo-header order)
- Enrich ClickHouse schema with IP/TCP metadata, H2 settings, Sec-* headers
- Ensure maximum data completeness: all available L3/L4, TLS, HTTP fields emitted

bot-detector:
- Replace logistic regression with MLP fusion classifier
- Replace KS drift detection with ADWIN online learning
- Replace NetworkX/Louvain with PyTorch Geometric GraphSAGE for fleet detection
- Replace autoencoder with RealNVP normalizing flow + SessionTransformer embeddings

infra:
- Add distributed E2E test infrastructure (4 VMs: endpoints + analysis)
- Add Vagrant provisioning for analysis VM, e2e Makefile targets, run scripts

docs:
- Restructure thesis into chapter files with corrected references
- Add E2E testing documentation
- Update architecture, schema, deployment, service docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 02:57:07 +02:00
f88b739992 feat(e2e): add distributed E2E test framework with parametric traffic generation
Add run-e2e-test.sh with CLI parameters (--hits, --http-ratio, --dns, --tls,
--src-ips, --keep-analysis, --up) for configurable traffic generation. Traffic
runs from VM endpoints with multiple source IPs (alias IPs on eth0) to produce
distinct sessions for the ML pipeline. Fix curl TLS flags (--tlsv1.2 instead
of --tls-v1-2), skip redundant local verification in distributed mode, and
fix dashboard is_available() cache that never retried after ClickHouse recovery.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 00:09:32 +02:00
7894d39f1c feat(ml): replace logistic regression with MLP fusion and KS drift with ADWIN online learning
Replace the LogisticRegression meta-learner with a PyTorch MetaFusionMLP
(Linear(3,16)->BN->ReLU->Dropout->Linear(16,1)->Sigmoid) for non-linear
fusion of EIF, NF, and XGBoost scores. Replace KS-test + quantile digest
drift detection with ADWIN (adaptive sliding window, Hoeffding bound).
Replace weekly XGBoost batch retraining with River HoeffdingAdaptiveTree
for incremental online learning (learn_one per cycle). Update all thesis
documentation sections (2.4.2c, 2.4.3, 3.8, discussion, conclusion).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 16:32:34 +02:00
c6cb12981c feat(ml): replace NetworkX/Louvain with PyTorch Geometric GraphSAGE for fleet detection
Rewrite fleet.py to use a GNN-based approach: nodes are src_ip with ML feature
vectors, edges connect IPs sharing (JA4, ASN) pairs, GraphSAGE (2 SAGEConv
layers, in→64→32) produces 32D embeddings clustered by HDBSCAN. PyG NeighborLoader
activates for >50k nodes. Update thesis docs (§5.2, §6.4, §2, §8) to reflect
GraphSAGE architecture and PyG scalability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 15:45:34 +02:00
c1821dcbc4 feat(ml): replace Autoencoder with RealNVP Normalizing Flow and add SessionTransformer embeddings
Replace TrafficAutoEncoder (MSE reconstruction scoring) with TrafficNormalizingFlow
(RealNVP via FrEIA, 4 affine coupling blocks, anomaly score = -log p(x)) for
mathematically rigorous density estimation. Add SessionTransformer module producing
32-dimensional sequence embeddings from raw HTTP request sequences (path, method,
timing) via a lightweight TransformerEncoder, replacing path_transition_entropy and
cadence_cv features. Update thesis documentation sections 2.4.2b and 3.8 accordingly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 15:11:21 +02:00
0e5f94dd0d docs: restructure thesis into chapter files with corrected references
Split monolithic thesis into separate chapter markdown files under
docs/thesis/. Remove fabricated bibliography entries, correct inflated
claims, add GNN/Transformers section, and rename MetaLearner to Fusion LR.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 13:51:38 +02:00
ac75ce2956 chore: remove regenerable data and build artifacts from git tracking
Add .gitignore rules for generated CSV data, eBPF compiled objects,
and vmlinux.h header. Remove 19 tracked files (~175 MB) that can be
regenerated from scripts (generate_*.py), bpftool, or bpf2go.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 02:31:27 +02:00
6e5eb38efd docs: update thesis and docs with Cleanlab label filtering integration
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 02:19:46 +02:00
9d27abf43c fix(ml): integrate Cleanlab to filter noisy SOC labels and prevent model poisoning
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 02:11:25 +02:00
c60ce97f23 feat(bot-detector): add dynamic browser profiling engine with HDBSCAN clustering
Implement offline profile building (profile_builder.py) and real-time
dynamic scoring (browser_matcher_dynamic.py) using HDBSCAN-based browser
fingerprint clustering. Add ClickHouse materialized view (13_h2_profiling.sql)
for h2_profile_stats aggregation. Update thesis and project documentation
to cover the new dynamic profiling architecture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 02:06:00 +02:00
64dada980f docs: synchronize thesis with codebase — browser_matcher, fleet detection, module counts
Update THESIS_HTTP_Traffic_Detection.md to reflect actual implementation state:
- Module count: 12 → 14 modules, 2 912 → 3 700 lines
- browser_matcher: [partiel] → [impl.] (all 7 scoring dimensions, Chrome/Firefox/Safari signatures)
- Fleet detection §5.2: HDBSCAN → Louvain with connected components fallback
- §3.9.1 H2 tables: update Firefox/Safari pseudo-header order to real captured values
- §7 conclusion: 96/0/0 features (all implemented, zero partial)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 01:38:57 +02:00
842b98d13a chore: remove stale binary blob from index
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 01:13:17 +02:00
6b244e307e chore: add .vagrant/machines to gitignore, remove orphan binary
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 01:12:12 +02:00
d75825278e feat: multi-distro VM tests, ja4ebpf eBPF improvements, bot-detector scoring
ja4ebpf:
- Refactor BPF TC capture with improved SYN offset handling and TCP option parsing
- Enhance TLS uprobe SSL hooking for better key extraction
- Add ClickHouse writer improvements for HTTP log materialized views
- Update RPM spec for Rocky Linux 8/9/10, fix systemd service
- Simplify loader with cleaner bpf2go integration

bot-detector:
- Add H2 SETTINGS per-parameter comparison in browser_matcher
- Enhance browser signatures and scoring pipeline
- Improve preprocessing and cycle detection

infra:
- Multi-distro Vagrantfile (centos8, rocky9, rocky10) with per-distro provisioning
- New Makefile targets: vm-up-all, test-vm-matrix, test-vm-centos8/rocky10
- Add debug helpers and run-test-from-host.sh for host-driven VM testing
- Update run-tests-vm.sh for cross-distro compatibility
- Remove accidental binary blob (\004)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 01:09:33 +02:00
d81463a589 fix(tests): rewrite test-rpm.sh for 3 distros × 3 stacks RPM validation
Complete rewrite of the RPM test harness to properly test ja4ebpf across
CentOS 8, Rocky Linux 9/10 with nginx, apache, and hitch+varnish stacks.

Key fixes:
- Replace nested heredoc-over-SSH with upload-then-execute script pattern
- Fix Apache SSLCertificateKey → SSLCertificateKeyFile directive
- Fix hitch config: backend string syntax, user=nobody, combined PEM, --daemon
- Fix timeout+shell-function incompatibility (timeout can't exec functions)
- Fix subshell result counting by parsing output file instead of variables

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 01:03:57 +02:00
957918c565 fix(ja4ebpf): Rocky Linux RPM builder, remove correlated field, fix thesis
- Dockerfile.package: migre go-builder de golang:bookworm (Debian) vers
  rockylinux:9, installe Go depuis le tarball officiel, remplace apt par
  dnf (clang llvm libbpf-devel bpftool)

- Suppression du champ 'correlated' de l'agent ja4ebpf : avec eBPF/XDP,
  la corrélation L3/L4↔L7 est toujours implicite par présence des champs.
  Supprimé de : session.go, manager.go, main.go (x5), clickhouse.go

- Thèse (6 corrections listées + cohérence correlated) :
  1. §3.5 + §3.9.1 : SSL_read retourne des octets bruts sans respecter les
     frontières H2 → buffer circulaire de réassemblage en Go userspace
  2. §3.1 : supprimé libpcap + CAP_NET_RAW, remplacé par définition uprobe
  3. §4 + §7 : compte exact 96 features en 8 familles (Famille 1–8),
     supprimé taxonomie F1–F11 obsolète, tous les totaux mis à jour
  4. §2.4 + §8 : remplacé 7 fausses URLs arXiv par [Référence à vérifier]
  5. §4 Famille 2 : ja4_drift_ratio → renvoi à Famille 8 (définition complète)
  6. §6.4 : ajouté limite 'Overhead de l'uprobe SSL_read'
  + §3.6 : supprimé correlated=0/1 du texte architectural

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-12 04:48:40 +02:00
b1218a2367 fix(ja4ebpf): fix TLS capture, SYN offsets, TCP option parsing
- Increase MAX_TLS_PAYLOAD from 512 to 2048 bytes to capture full
  TLS ClientHellos (modern browsers/curl send 1000-1543 byte ClientHellos)
- Fix ParseClientHello to tolerate XDP-truncated payloads: clamp
  recordLength and chLen to available data instead of returning error
- Fix cipher suites, compression, extensions truncation to use clamping
- Fix consumeSynEvents struct field offsets: dst_ip (4 bytes at offset 4)
  was not accounted for, causing all L3/L4 metadata to be read from
  wrong positions (TTL was actually dst_ip[0], windowSize was dst_port, etc.)
- Add parseTCPOptions() to extract MSS and Window Scale from raw TCP options
  (C code sets defaults of mss=0, window_scale=0xFF, expects Go to parse)
- Fix consumeAcceptEvents: skip zero-IP events to avoid phantom sessions
- Fix consumeSSLEvents: filter zero-IP/port events when proc fallback fails
- Add missing consumeHTTPPlainEvents goroutine (was defined but never called)
- Fix race condition: SYN consumer sets Correlated=true if TLS already present
- Update tls_hello_event struct offsets in Go consumer (payload_len now at
  offset 2054, was 518, due to payload array growing from 512 to 2048 bytes)
- Remove debug logging from consumers and GC

E2E verified: HTTP plain (port 80) and HTTPS (port 443) both produce
fully correlated sessions in ClickHouse with correct:
  - ip_meta_ttl=64, ip_meta_df=true, ip_meta_id
  - tcp_meta_window_size=64240, tcp_meta_window_scale=10, tcp_meta_mss=1460
  - ja4=t13i3010_1d37bd780c83_95d2a80e6515
  - tls_alpn=http/1.1
  - method=GET, path=/, header_order_signature=Host;User-Agent;Accept
  - correlated=1

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-12 04:16:44 +02:00
f85a10b012 feat: pipeline L7 HTTP complet + infrastructure tests VM
Correctifs pipeline L7 (uprobe SSL_read) :
- uprobe_ssl.c : ssl_set_fd ne retourne plus tôt quand fd_conn_map est
  vide (accept4 non disponible en Docker). Sauvegarde ssl_ptr→{fd,0,0}
  pour permettre le fallback /proc côté Go.
- main.go : consumeSSLEvents reécrit avec routeur magic-bytes complet :
  * HTTP/2 preface → extraction SETTINGS + conversion correlation.HTTP2Settings
  * HTTP/1.x requête → method, path, query, headers, header_order_sig
  * HTTP/1.x réponse → status_code
  * Fallback /proc/<tgid>/fd/<fd> quand src_ip=0 (accept4 absent)
- writer/clickhouse.go : export header_order_signature ajouté

Nouveaux packages :
- internal/parser/http1.go : parseur HTTP/1.x (IsHTTP1Request,
  ParseHTTP1Request, IsHTTP1Response, ParseHTTP1Response)
- internal/parser/http1_test.go : 11 tests unitaires (28 total passent)
- internal/procutil/proc_lookup.go : résolution fd→IP via /proc avec cache
  TTL 5s (FDCache). Supporte /proc/PID/net/tcp et tcp6, IPv4-mappé IPv6.

Infrastructure tests VM (tests/vm/) :
- Vagrantfile : VM Rocky Linux 9 KVM, 4 CPU / 4 GB RAM
- provision.sh : installation toolchain eBPF + Go + Docker + nginx
- run-tests-vm.sh : suite de test complète dans la VM (L3/L4+TLS+L7)
- README.md : guide d'installation et d'utilisation
- Makefile : cibles vm-up, vm-down, vm-ssh, test-vm-nginx, test-vm-all,
  vm-rebuild-ja4ebpf

Corrections stack Docker :
- Dockerfiles nginx/apache/nginx-varnish/hitch-varnish : suppression des
  références à shared/go/ja4common/ (répertoire supprimé)
- clickhouse-init.sh : restauré depuis git, seed anubis_ua_rules obsolète
  supprimé (table REGEXP_TREE supprimée du schéma)
- traffic-gen : ajout HTTP/1.0 (http.client) et HTTP/2 (httpx)
- verify_db.py : script de vérification 35 checks (L3/L4/TLS/L7/corrélation)
- run-stack-tests.sh : phase 6 verify_db ajoutée

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-12 02:37:00 +02:00
9734e21fe3 chore: suppression des services obsolètes (sentinel, correlator, mod-reqin-log)
Remplacés par l'agent ja4ebpf (eBPF CO-RE). Nettoyage complet :

Supprimé :
- old/ (archive de l'ancienne architecture)
- services/correlator/ (logcorrelator Go)
- services/sentinel/ (capture pcap Go)
- services/mod-reqin-log/ (module Apache C)
- shared/go/ja4common/ (lib Go partagée — plus importée par ja4ebpf)
- tests/integration/platform/ (test correlator+sentinel+httpd)
- tests/integration/docker-compose.yml (compose ancienne archi)
- tests/integration/run-tests.sh (runner correlator/sentinel)
- tests/integration/verify_mvs.py (script orphelin)

Nettoyé :
- go.work : retire ./shared/go/ja4common
- services/ja4ebpf/go.mod : retire replace ja4common (jamais importé)
- services/ja4ebpf/Dockerfile* : retire les COPY ja4common inutiles
- Makefile : retire test-ja4common-python, test-integration*, targets obsolètes
- tests/integration/README.md : réécrit pour l'architecture ja4ebpf

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-12 01:48:14 +02:00
dc6ffd6474 fix: tests intégration matrix — procps-ng, varnish h2, hitch ALPN, pgrep→ps
- Ajout de procps-ng dans les 4 Dockerfiles runtime (ps/pgrep disponibles)
- Remplacement de pgrep par ps -C dans tous les run-tests.sh
- Correction entrypoint nginx-varnish : pgrep nginx → cat nginx.pid (exit 127)
- Activation HTTP/2 dans Varnish : ajout de -p feature=+http2 dans les
  entrypoints nginx-varnish et hitch-varnish
- Restauration ALPN h2,http/1.1 dans hitch.conf (varnish supporte maintenant h2)
- Correction healthcheck hitch-varnish : curl sans --http1.1 (h2 fonctionnel)
- Correction requêtes phase_verify : http_logs_raw → http_logs, colonnes correctes
- Correction writer clickhouse.go : noms JSON alignés avec la MV (ip_meta_*, tls_sni…)
- Fix toStartOfSecond(DateTime) → toStartOfSecond(toDateTime64(col, 3))
- Retrait du SKIP el8/nginx-varnish (varnish s'installe bien sur AlmaLinux 8)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-12 01:29:01 +02:00
3b047b680a fix(ja4ebpf): split bpf2go generate into Ja4Tc + Ja4Ssl, fix RPM systemd-rpm-macros
- Use two separate //go:generate directives (Ja4Tc for tc_capture.c, Ja4Ssl
  for uprobe_ssl.c) to avoid duplicate LICENSE symbol and multi-file clang issue
- Update loader.go to hold tcObjs/sslObjs separately with correct field names:
  UprobeSslSetFd, UprobeSslReadEntry, UretprobeSslReadExit,
  KprobeAccept4Entry, KretprobeAccept4Exit
- Add systemd-rpm-macros to all three RPM build stages (el8/el9/el10)
  so that %{_unitdir} macro resolves correctly
- RPMs now build successfully for el8, el9, el10

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-11 23:21:11 +02:00
a1e4c1dad5 feat: add ja4ebpf service — eBPF-based TLS/TCP fingerprinting daemon
- TC ingress hook captures TCP SYN (L3/L4) and TLS ClientHello
- Uprobes on SSL_read/SSL_set_fd capture decrypted TLS data
- Kprobes on accept4 correlate socket FDs to client IP:port
- JA4 fingerprint computed from parsed TLS ClientHello
- HTTP/2 SETTINGS and WINDOW_UPDATE extracted from decrypted streams
- Session manager with sharded map (256 shards) and GC goroutine
- Slowloris detection: sessions with no requests after 10s threshold
- ClickHouse batch writer to ja4_logs.http_logs_raw (raw_json)
- All tests pass: 17 parser + 10 correlation tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-11 22:43:26 +02:00
7eb3ad21fd feat(dashboard): afficher SETTINGS H2 individuels dans la table mismatch
- /api/browser-signatures : top_mismatches inclut désormais les 7 colonnes
  SETTINGS individuelles (h2_header_table_size, h2_enable_push,
  h2_max_concurrent_streams, h2_initial_window_size, h2_max_frame_size,
  h2_max_header_list_size, h2_enable_connect_protocol)
- stats : ajout sessions_with_priority (countIf h2_priority_present > 0)
- browsers.html : colonne SETTINGS compact dans la table suspects
  (format '3:100, 4:65536, 2:0' — IDs Akamai avec valeurs non-nulles)
- Compteur pseudo-priority utilise la vraie valeur sessions_with_priority
  au lieu d'afficher '—'

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-11 03:11:17 +02:00
f704541f83 feat(h2): direct per-parameter SETTINGS comparison in browser_matcher
- Rewrote _d1_h2_settings() with 3-signal weighted formula:
  direct_score×0.60 + dict_match×0.30 + ja4_coherence×0.10
  when individual SETTINGS cols are available in the DataFrame
- Added _H2_SETTINGS_COLS dict (IDs 1,2,3,4,5,6,8 → column names)
- Fallback to dict_match×0.80 + ja4_coherence×0.20 for backward compat
- Fix view_ai_features_1h: pass 7 individual SETTINGS columns through
  base_data CTE (h2_header_table_size, h2_enable_push,
  h2_max_concurrent_streams, h2_initial_window_size, h2_max_frame_size,
  h2_max_header_list_size, h2_enable_connect_protocol)
- Remove non-existent h2_dict_confidence reference from view SQL
  (dict_browser_h2 only exposes browser_family attribute)
- Add 7 new pytest cases: exact match, one wrong setting, forbidden key
  penalty, unknown fingerprint with correct settings, fallback path,
  CDN proxy neutralisation, full Chrome simulation
- 53/53 bot-detector tests pass
- Update thesis §3.9.2: document direct comparison algorithm + fallback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-11 03:05:36 +02:00
95e87149aa docs: mise à jour thèse — capture HTTP/2 passive et colonnes individuelles
Sections mises à jour :
- Architecture §3.1 : H2 capture déplacé de ja4sentinel vers mod_reqin_log
- §3.5 Couche L7 : description du hook process_connection (APR_HOOK_FIRST,
  AP_MODE_SPECULATIVE), parsing preface, notes c1/c2, colonnes individuelles
- Tableau des 12 colonnes H2 dans ja4_logs.http_logs avec types et conventions
- §3.9 Browser Matcher : statut mis à jour (capture [impl.], scoring [partiel])
- §3.9.1 : mécanisme de capture via process_connection au lieu de filtre connexion
- §3.9.4 : h2_window_update_value, h2_has_priority, h2_pseudo_order → [impl.]
- §6.6 Roadmap : dépendance capture H2 résolue, travail restant = browser_match_*
- Contribution 3 : description de l implémentation technique ajoutée
- Tableau récapitulatif : F8 passe de 0/8/0 à 3/5/0 (70 impl. / 5 partiel)
- Résumé quantitatif : 82% impl. (était 79%), 6% partiel (était 9%)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-11 02:40:41 +02:00