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>
This commit is contained in:
toto
2026-04-11 22:43:26 +02:00
parent 7eb3ad21fd
commit a1e4c1dad5
24 changed files with 3984 additions and 0 deletions

View File

@ -0,0 +1,86 @@
Name: ja4ebpf
Version: %{build_version}
Release: 1%{?dist}
Summary: JA4 eBPF Network Fingerprint Agent
License: Proprietary
URL: https://github.com/antitbone/ja4-platform
Source0: ja4ebpf
Source1: ja4ebpf.service
Source2: config.yml.example
# ── Compatibilité : RHEL/CentOS/Rocky/AlmaLinux 8 → 10 ───────────────────
# Binaire statique (CGO_ENABLED=0) : aucune dépendance de bibliothèque partagée.
# BTF natif disponible sur tous les kernels RHEL 8+ (backport dans 4.18).
BuildArch: x86_64
Requires: systemd
%description
ja4ebpf est un agent de collecte passif basé sur eBPF qui capture les
métadonnées réseau (L3/L4/L5/L7) pour le pipeline de détection de bots JA4.
Il utilise :
- Des hooks TC ingress pour les TCP SYN, TLS ClientHello, HTTP clair (80/8080)
- Des uprobes sur SSL_read/SSL_write pour le trafic HTTPS déchiffré
Le binaire est compilé statique et supporte RHEL/CentOS/Rocky/AlmaLinux 8 à 10.
%prep
# Binaire pré-compilé fourni dans Source0 (compilé par Dockerfile.package).
%build
# Compilation déléguée au Dockerfile.package multi-stage.
%install
rm -rf %{buildroot}
install -D -m 0755 %{SOURCE0} %{buildroot}%{_sbindir}/ja4ebpf
install -D -m 0640 %{SOURCE2} %{buildroot}%{_sysconfdir}/ja4ebpf/config.yml.example
install -D -m 0644 %{SOURCE1} %{buildroot}%{_unitdir}/ja4ebpf.service
install -d -m 0750 %{buildroot}%{_localstatedir}/lib/ja4ebpf
install -d -m 0750 %{buildroot}%{_localstatedir}/log/ja4ebpf
%pre
getent group ja4ebpf >/dev/null 2>&1 || \
groupadd -r -g 490 ja4ebpf
getent passwd ja4ebpf >/dev/null 2>&1 || \
useradd -r -u 490 -g ja4ebpf \
-d %{_localstatedir}/lib/ja4ebpf \
-s /sbin/nologin \
-c "JA4 eBPF agent" \
ja4ebpf
exit 0
%post
%systemd_post ja4ebpf.service
if [ ! -f %{_sysconfdir}/ja4ebpf/config.yml ]; then
cp -p %{_sysconfdir}/ja4ebpf/config.yml.example \
%{_sysconfdir}/ja4ebpf/config.yml
chown root:ja4ebpf %{_sysconfdir}/ja4ebpf/config.yml
chmod 640 %{_sysconfdir}/ja4ebpf/config.yml
fi
chown -R ja4ebpf:ja4ebpf \
%{_localstatedir}/lib/ja4ebpf \
%{_localstatedir}/log/ja4ebpf
%preun
%systemd_preun ja4ebpf.service
%postun
%systemd_postun_with_restart ja4ebpf.service
%files
%defattr(-,root,root,-)
%attr(0755, root, root) %{_sbindir}/ja4ebpf
%dir %attr(0750, root, ja4ebpf) %{_sysconfdir}/ja4ebpf
%config(noreplace) %attr(0640, root, ja4ebpf) %{_sysconfdir}/ja4ebpf/config.yml.example
%{_unitdir}/ja4ebpf.service
%dir %attr(0750, ja4ebpf, ja4ebpf) %{_localstatedir}/lib/ja4ebpf
%dir %attr(0750, ja4ebpf, ja4ebpf) %{_localstatedir}/log/ja4ebpf
%changelog
* %(date "+%a %b %d %Y") Build System <build@antitbone.local> - %{build_version}-1
- Build automatique via Dockerfile.package

View File

@ -0,0 +1,80 @@
# =============================================================================
# ja4ebpf.service — Unité systemd pour l'agent eBPF ja4ebpf
#
# Installation :
# install -m 644 ja4ebpf.service /usr/lib/systemd/system/
# systemctl daemon-reload
# systemctl enable --now ja4ebpf
#
# Sécurité :
# L'agent fonctionne sous un compte dédié "ja4ebpf" sans shell ni home.
# Les capabilities Linux strictement nécessaires sont accordées via
# AmbientCapabilities + CapabilityBoundingSet (sans User=root).
# Cible : RHEL/CentOS/Rocky/Alma 8+ (kernel ≥ 4.18, BTF natif disponible).
# =============================================================================
[Unit]
Description=JA4 eBPF Network Fingerprint Agent
Documentation=https://github.com/antitbone/ja4-platform
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=ja4ebpf
Group=ja4ebpf
ExecStart=/usr/sbin/ja4ebpf -config /etc/ja4ebpf/config.yml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
TimeoutStopSec=30s
# ── Capabilities Linux ─────────────────────────────────────────────────────
# CAP_BPF : charger/créer des programmes et maps eBPF (kernel ≥ 5.8)
# Sur RHEL 8 (4.18), cilium/ebpf retombe sur CAP_SYS_ADMIN.
# CAP_NET_ADMIN : attacher un programme TC sur une interface réseau
# CAP_NET_RAW : accès raw socket (fallback sur kernels sans TCX)
# CAP_PERFMON : attacher des perf_events / uprobes (kernel ≥ 5.8)
# CAP_SYS_ADMIN : requis sur RHEL 8 / kernel < 5.8 pour charger eBPF
# CAP_SYS_PTRACE : résoudre les offsets de fonctions pour les uprobes
# CAP_DAC_READ_SEARCH : lire /proc/<pid>/maps pour localiser libssl.so
CapabilityBoundingSet=CAP_BPF CAP_NET_ADMIN CAP_NET_RAW CAP_PERFMON CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
AmbientCapabilities=CAP_BPF CAP_NET_ADMIN CAP_NET_RAW CAP_PERFMON CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
# Ne jamais acquérir de nouveaux privilèges via setuid/setgid
NoNewPrivileges=yes
# ── Isolation du système de fichiers ───────────────────────────────────────
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/ja4ebpf /var/log/ja4ebpf /run/ja4ebpf
PrivateTmp=yes
PrivateDevices=no
# ── Isolation réseau ───────────────────────────────────────────────────────
PrivateNetwork=no
# ── Divers ─────────────────────────────────────────────────────────────────
ProtectKernelTunables=no
ProtectKernelModules=yes
ProtectKernelLogs=no
ProtectControlGroups=yes
RestrictNamespaces=yes
LockPersonality=yes
MemoryDenyWriteExecute=no # JIT eBPF requiert des mappings exécutables kernel-side
# ── Limites de ressources ──────────────────────────────────────────────────
LimitMEMLOCK=infinity
LimitNOFILE=65536
# ── Journalisation ─────────────────────────────────────────────────────────
StandardOutput=journal
StandardError=journal
SyslogIdentifier=ja4ebpf
WorkingDirectory=/var/lib/ja4ebpf
RuntimeDirectory=ja4ebpf
RuntimeDirectoryMode=0750
[Install]
WantedBy=multi-user.target