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>
This commit is contained in:
toto
2026-04-11 23:21:11 +02:00
parent a1e4c1dad5
commit 3b047b680a
155 changed files with 197011 additions and 599 deletions

View File

@ -45,7 +45,7 @@ COPY services/ja4ebpf/ ./services/ja4ebpf/
WORKDIR /build/services/ja4ebpf
# Génération des bindings eBPF (C → bytecode embarqué en Go)
RUN go generate ./internal/loader/
RUN GOWORK=off go generate ./internal/loader/
# Compilation statique
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
@ -56,7 +56,7 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
# ── Stage 2 : RPM pour el8 ───────────────────────────────────────────────
FROM almalinux:8 AS rpm-el8
RUN dnf install -y rpm-build rpmdevtools && dnf clean all && rpmdev-setuptree
RUN dnf install -y rpm-build rpmdevtools systemd-rpm-macros && dnf clean all && rpmdev-setuptree
COPY --from=go-builder /out/ja4ebpf /root/rpmbuild/SOURCES/ja4ebpf
COPY services/ja4ebpf/packaging/systemd/ja4ebpf.service /root/rpmbuild/SOURCES/ja4ebpf.service
@ -72,7 +72,7 @@ RUN rpmbuild -bb \
# ── Stage 3 : RPM pour el9 ───────────────────────────────────────────────
FROM rockylinux:9 AS rpm-el9
RUN dnf install -y rpm-build rpmdevtools && dnf clean all && rpmdev-setuptree
RUN dnf install -y rpm-build rpmdevtools systemd-rpm-macros && dnf clean all && rpmdev-setuptree
COPY --from=go-builder /out/ja4ebpf /root/rpmbuild/SOURCES/ja4ebpf
COPY services/ja4ebpf/packaging/systemd/ja4ebpf.service /root/rpmbuild/SOURCES/ja4ebpf.service
@ -88,7 +88,7 @@ RUN rpmbuild -bb \
# ── Stage 4 : RPM pour el10 ──────────────────────────────────────────────
FROM almalinux:10 AS rpm-el10
RUN dnf install -y rpm-build rpmdevtools && dnf clean all && rpmdev-setuptree
RUN dnf install -y rpm-build rpmdevtools systemd-rpm-macros && dnf clean all && rpmdev-setuptree
COPY --from=go-builder /out/ja4ebpf /root/rpmbuild/SOURCES/ja4ebpf
COPY services/ja4ebpf/packaging/systemd/ja4ebpf.service /root/rpmbuild/SOURCES/ja4ebpf.service

View File

@ -4,9 +4,26 @@
#pragma once
/* vmlinux.h (inclus dans les .c) fournit déjà tous les types kernel (__u32, etc.).
* N'inclure linux/types.h que si vmlinux.h n'est pas présent. */
#ifndef __VMLINUX_H__
#include <linux/types.h>
#endif
#include <bpf/bpf_helpers.h>
/* Constantes TC (linux/pkt_cls.h) — non incluses via vmlinux.h */
#ifndef TC_ACT_OK
#define TC_ACT_UNSPEC (-1)
#define TC_ACT_OK 0
#define TC_ACT_RECLASSIFY 1
#define TC_ACT_SHOT 2
#define TC_ACT_PIPE 3
#define TC_ACT_STOLEN 4
#define TC_ACT_QUEUED 5
#define TC_ACT_REPEAT 6
#define TC_ACT_REDIRECT 7
#endif
/* ---------------------------------------------------------------------------
* Événement TCP SYN : émis pour chaque nouvelle connexion TCP observée
* ---------------------------------------------------------------------------*/

File diff suppressed because it is too large Load Diff

View File

@ -16,12 +16,14 @@ import (
"github.com/cilium/ebpf/rlimit"
)
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -target amd64 -cflags "-O2 -g -Wall -Werror -D__TARGET_ARCH_x86" Ja4eBPF ../../bpf/tc_capture.c ../../bpf/uprobe_ssl.c -- -I../../bpf/headers
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -target amd64 -cflags "-O2 -g -Wall -D__TARGET_ARCH_x86 -Wno-pass-failed" Ja4Tc ../../bpf/tc_capture.c -- -I../../bpf/headers
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -target amd64 -cflags "-O2 -g -Wall -D__TARGET_ARCH_x86 -Wno-pass-failed" Ja4Ssl ../../bpf/uprobe_ssl.c -- -I../../bpf/headers
// Loader encapsule les objets eBPF compilés, les liens vers les hooks,
// et les readers RingBuffer exposés au pipeline de traitement.
type Loader struct {
objs *Ja4eBPFObjects // généré par bpf2go
tcObjs *Ja4TcObjects // généré par bpf2go (tc_capture.c)
sslObjs *Ja4SslObjects // généré par bpf2go (uprobe_ssl.c)
tcLink link.Link
uprobeLinks []link.Link
@ -50,56 +52,68 @@ func New() (*Loader, error) {
return nil, fmt.Errorf("suppression RLIMIT_MEMLOCK: %w", err)
}
objs := &Ja4eBPFObjects{}
// Charger le bytecode eBPF compilé (embarqué par bpf2go).
// nil = cilium/ebpf résout le BTF kernel natif automatiquement.
if err := LoadJa4eBPFObjects(objs, nil); err != nil {
return nil, fmt.Errorf("chargement objets eBPF: %w", err)
// Charger les objets TC (tc_capture.c)
tcObjs := &Ja4TcObjects{}
if err := LoadJa4TcObjects(tcObjs, nil); err != nil {
return nil, fmt.Errorf("chargement objets TC eBPF: %w", err)
}
// Charger les objets SSL/uprobe (uprobe_ssl.c)
sslObjs := &Ja4SslObjects{}
if err := LoadJa4SslObjects(sslObjs, nil); err != nil {
tcObjs.Close()
return nil, fmt.Errorf("chargement objets SSL eBPF: %w", err)
}
// Initialiser les readers pour chaque ring buffer
synReader, err := ringbuf.NewReader(objs.RbTcpSyn)
synReader, err := ringbuf.NewReader(tcObjs.RbTcpSyn)
if err != nil {
objs.Close()
sslObjs.Close()
tcObjs.Close()
return nil, fmt.Errorf("création reader rb_tcp_syn: %w", err)
}
tlsReader, err := ringbuf.NewReader(objs.RbTlsHello)
tlsReader, err := ringbuf.NewReader(tcObjs.RbTlsHello)
if err != nil {
synReader.Close()
objs.Close()
sslObjs.Close()
tcObjs.Close()
return nil, fmt.Errorf("création reader rb_tls_hello: %w", err)
}
sslReader, err := ringbuf.NewReader(objs.RbSslData)
httpPlainReader, err := ringbuf.NewReader(tcObjs.RbHttpPlain)
if err != nil {
tlsReader.Close()
synReader.Close()
objs.Close()
return nil, fmt.Errorf("création reader rb_ssl_data: %w", err)
}
acceptReader, err := ringbuf.NewReader(objs.RbAccept)
if err != nil {
sslReader.Close()
tlsReader.Close()
synReader.Close()
objs.Close()
return nil, fmt.Errorf("création reader rb_accept: %w", err)
}
httpPlainReader, err := ringbuf.NewReader(objs.RbHttpPlain)
if err != nil {
acceptReader.Close()
sslReader.Close()
tlsReader.Close()
synReader.Close()
objs.Close()
sslObjs.Close()
tcObjs.Close()
return nil, fmt.Errorf("création reader rb_http_plain: %w", err)
}
sslReader, err := ringbuf.NewReader(sslObjs.RbSslData)
if err != nil {
httpPlainReader.Close()
tlsReader.Close()
synReader.Close()
sslObjs.Close()
tcObjs.Close()
return nil, fmt.Errorf("création reader rb_ssl_data: %w", err)
}
acceptReader, err := ringbuf.NewReader(sslObjs.RbAccept)
if err != nil {
sslReader.Close()
httpPlainReader.Close()
tlsReader.Close()
synReader.Close()
sslObjs.Close()
tcObjs.Close()
return nil, fmt.Errorf("création reader rb_accept: %w", err)
}
return &Loader{
objs: objs,
tcObjs: tcObjs,
sslObjs: sslObjs,
SynReader: synReader,
TLSReader: tlsReader,
SSLReader: sslReader,
@ -120,7 +134,7 @@ func (l *Loader) AttachTC(iface string) error {
// Attacher le programme TC en ingress via TCX
lnk, err := link.AttachTCX(link.TCXOptions{
Interface: netIface.Index,
Program: l.objs.CaptureTcIngress,
Program: l.tcObjs.CaptureTcIngress,
Attach: ebpf.AttachTCXIngress,
})
if err != nil {
@ -146,21 +160,21 @@ func (l *Loader) AttachUprobes(sslLibPath string) error {
}
// Uprobe sur SSL_set_fd (entry)
setFdLink, err := ex.Uprobe("SSL_set_fd", l.objs.UprobeSSLSetFd, nil)
setFdLink, err := ex.Uprobe("SSL_set_fd", l.sslObjs.UprobeSslSetFd, nil)
if err != nil {
return fmt.Errorf("attachement uprobe SSL_set_fd: %w", err)
}
l.uprobeLinks = append(l.uprobeLinks, setFdLink)
// Uprobe sur SSL_read (entry)
readEntryLink, err := ex.Uprobe("SSL_read", l.objs.UprobeSSLReadEntry, nil)
readEntryLink, err := ex.Uprobe("SSL_read", l.sslObjs.UprobeSslReadEntry, nil)
if err != nil {
return fmt.Errorf("attachement uprobe SSL_read (entry): %w", err)
}
l.uprobeLinks = append(l.uprobeLinks, readEntryLink)
// Uretprobe sur SSL_read (exit)
readExitLink, err := ex.Uretprobe("SSL_read", l.objs.UretprobeSSLReadExit, nil)
readExitLink, err := ex.Uretprobe("SSL_read", l.sslObjs.UretprobeSslReadExit, nil)
if err != nil {
return fmt.Errorf("attachement uretprobe SSL_read (exit): %w", err)
}
@ -172,14 +186,14 @@ func (l *Loader) AttachUprobes(sslLibPath string) error {
// AttachAcceptProbe attache les kprobes sur l'appel système accept4.
func (l *Loader) AttachAcceptProbe() error {
// Kprobe à l'entrée d'accept4
kpEntry, err := link.Kprobe("accept4", l.objs.KprobeAccept4Entry, nil)
kpEntry, err := link.Kprobe("accept4", l.sslObjs.KprobeAccept4Entry, nil)
if err != nil {
return fmt.Errorf("attachement kprobe accept4 (entry): %w", err)
}
l.uprobeLinks = append(l.uprobeLinks, kpEntry)
// Kretprobe à la sortie d'accept4
kpExit, err := link.Kretprobe("accept4", l.objs.KretprobeAccept4Exit, nil)
kpExit, err := link.Kretprobe("accept4", l.sslObjs.KretprobeAccept4Exit, nil)
if err != nil {
return fmt.Errorf("attachement kretprobe accept4 (exit): %w", err)
}
@ -220,8 +234,11 @@ func (l *Loader) Close() error {
}
// Libérer les objets eBPF (maps, programmes)
if l.objs != nil {
l.objs.Close()
if l.sslObjs != nil {
l.sslObjs.Close()
}
if l.tcObjs != nil {
l.tcObjs.Close()
}
return nil