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>
This commit is contained in:
@ -93,8 +93,9 @@ Les uprobes s'attachent dynamiquement aux fonctions OpenSSL dans `libssl.so` :
|
||||
| `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 recvfrom (Nginx HTTP en clair)
|
||||
### 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 |
|
||||
@ -102,10 +103,18 @@ Les hooks `sys_enter_recvfrom` / `sys_exit_recvfrom` capturent les appels systè
|
||||
| `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 |
|
||||
|
||||
**Note** : Le kretprobe sur `__x64_sys_recvfrom` remplace le tracepoint `sys_exit_recvfrom` qui échouait avec "permission denied" sur Rocky Linux 9+.
|
||||
|
||||
**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 `sys_enter_read` / `sys_exit_read` capturent les appels système `read()` du serveur Apache httpd pour capturer le trafic HTTP en clair complet :
|
||||
|
||||
| Hook | Type | État | Rôle |
|
||||
|------|------|------|------|
|
||||
| `tp_syscalls_sys_enter_read` | tracepoint | ✅ Fonctionnel | Sauvegarde les arguments read (fd, buf, count) |
|
||||
| `kretprobe___x64_sys_read` | kretprobe | ✅ Fonctionnel | Capture les données lues + émet vers pb_apache_http |
|
||||
|
||||
**Filtrage par PID Apache** : La map `apache_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)
|
||||
|
||||
@ -65,8 +65,7 @@ type Config struct {
|
||||
|
||||
Uprobes struct {
|
||||
Enabled bool `yaml:"enabled"` // activer l'attachement automatique des uprobes
|
||||
NginxBinPath string `yaml:"nginx_bin_path"` // chemin vers le binaire nginx
|
||||
ApacheEnabled bool `yaml:"apache_enabled"` // activer la capture Apache httpd
|
||||
Servers []string `yaml:"servers"` // serveurs web à capturer: ["nginx", "apache", "both"]
|
||||
MaxRetries int `yaml:"max_retries"` // nombre de tentatives d'attachement (défaut: 30)
|
||||
RetryIntervalSec int `yaml:"retry_interval_sec"` // intervalle entre tentatives (défaut: 2)
|
||||
} `yaml:"uprobes"`
|
||||
@ -89,8 +88,7 @@ func loadConfig(path string) (*Config, error) {
|
||||
cfg.Log.Level = "info"
|
||||
cfg.Log.Format = "json"
|
||||
cfg.Uprobes.Enabled = false
|
||||
cfg.Uprobes.NginxBinPath = "/usr/sbin/nginx"
|
||||
cfg.Uprobes.ApacheEnabled = false
|
||||
cfg.Uprobes.Servers = []string{} // vide par défaut, peut être ["nginx"], ["apache"], ["nginx", "apache"]
|
||||
cfg.Uprobes.MaxRetries = 30
|
||||
cfg.Uprobes.RetryIntervalSec = 2
|
||||
|
||||
@ -140,11 +138,17 @@ func loadConfig(path string) (*Config, error) {
|
||||
if v := os.Getenv("JA4EBPF_UPROBES_ENABLED"); v != "" {
|
||||
cfg.Uprobes.Enabled = strings.EqualFold(v, "true") || v == "1" || v == "yes"
|
||||
}
|
||||
if v := os.Getenv("JA4EBPF_NGINX_BIN_PATH"); v != "" {
|
||||
cfg.Uprobes.NginxBinPath = v
|
||||
if v := os.Getenv("JA4EBPF_UPROBES_SERVERS"); v != "" {
|
||||
// Accepte une liste séparée par des virgules: "nginx,apache" ou "both" pour les deux
|
||||
if strings.EqualFold(v, "both") {
|
||||
cfg.Uprobes.Servers = []string{"nginx", "apache"}
|
||||
} else {
|
||||
cfg.Uprobes.Servers = strings.Split(v, ",")
|
||||
// Normaliser les noms de serveurs (trim whitespace et lowercase)
|
||||
for i, s := range cfg.Uprobes.Servers {
|
||||
cfg.Uprobes.Servers[i] = strings.ToLower(strings.TrimSpace(s))
|
||||
}
|
||||
}
|
||||
if v := os.Getenv("JA4EBPF_APACHE_ENABLED"); v != "" {
|
||||
cfg.Uprobes.ApacheEnabled = strings.EqualFold(v, "true") || v == "1" || v == "yes"
|
||||
}
|
||||
|
||||
|
||||
@ -295,16 +299,22 @@ func main() {
|
||||
log.Printf("[ja4ebpf] avertissement tracepoint accept4: %v", err)
|
||||
}
|
||||
|
||||
// --- 4b. Attachement uprobes nginx (avec retry automatique) ---
|
||||
// --- 4b. Attachement uprobes serveurs web (nginx, apache) ---
|
||||
if cfg.Uprobes.Enabled && len(cfg.Uprobes.Servers) > 0 {
|
||||
for _, server := range cfg.Uprobes.Servers {
|
||||
switch server {
|
||||
case "nginx":
|
||||
if err := attachNginxUprobesWithRetry(ctx, ldr, cfg); err != nil {
|
||||
log.Printf("[ja4ebpf] erreur attachement uprobes nginx: %v", err)
|
||||
}
|
||||
|
||||
// --- 4c. Attachement uprobes Apache httpd ---
|
||||
if cfg.Uprobes.ApacheEnabled {
|
||||
case "apache":
|
||||
if err := attachApacheUprobesWithRetry(ctx, ldr, cfg); err != nil {
|
||||
log.Printf("[ja4ebpf] erreur attachement uprobes Apache: %v", err)
|
||||
}
|
||||
default:
|
||||
log.Printf("[ja4ebpf] avertissement: serveur web inconnu '%s' (options: nginx, apache)", server)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -351,10 +361,15 @@ func main() {
|
||||
// --- 8. Compteurs d'événements consommés (mode debug) ---
|
||||
consumed := &eventCounters{}
|
||||
|
||||
// Démarrer les goroutines de consommation pour chaque serveur configuré
|
||||
for _, server := range cfg.Uprobes.Servers {
|
||||
switch server {
|
||||
case "nginx":
|
||||
go consumeNginxHTTPEvents(ctx, ldr.NginxHTTPReader, mgr, &consumed.nginx)
|
||||
if cfg.Uprobes.ApacheEnabled {
|
||||
case "apache":
|
||||
go consumeApacheHTTPEvents(ctx, ldr.ApacheHTTPReader, mgr, &consumed.apache)
|
||||
}
|
||||
}
|
||||
|
||||
// --- 9. Goroutines de consommation des ring buffers ---
|
||||
go consumeSynEvents(ctx, ldr.SynReader, mgr, &consumed.syn)
|
||||
@ -1225,14 +1240,9 @@ func updateH2Settings(last *correlation.HTTPRequest, settings *parser.HTTP2Setti
|
||||
// Retente jusqu'à maxRetries fois toutes les retryInterval secondes.
|
||||
// Utile pour attendre que nginx démarre après ja4ebpf.
|
||||
func attachNginxUprobesWithRetry(ctx context.Context, l *loader.Loader, cfg *Config) error {
|
||||
if !cfg.Uprobes.Enabled {
|
||||
log.Printf("[uprobes] nginx uprobes désactivés (uprobes.enabled=false)")
|
||||
return nil
|
||||
}
|
||||
|
||||
binPath := cfg.Uprobes.NginxBinPath
|
||||
maxRetries := cfg.Uprobes.MaxRetries
|
||||
retryInterval := time.Duration(cfg.Uprobes.RetryIntervalSec) * time.Second
|
||||
binPath := "/usr/sbin/nginx" // chemin par défaut pour nginx
|
||||
|
||||
log.Printf("[uprobes] tentative d'attachement nginx uprobes (bin=%s, max_retries=%d, interval=%v)",
|
||||
binPath, maxRetries, retryInterval)
|
||||
@ -1275,10 +1285,9 @@ func attachNginxUprobesWithRetry(ctx context.Context, l *loader.Loader, cfg *Con
|
||||
// Retente jusqu'à maxRetries fois toutes les retryInterval secondes.
|
||||
// Utile pour attendre que Apache httpd démarre après ja4ebpf.
|
||||
func attachApacheUprobesWithRetry(ctx context.Context, l *loader.Loader, cfg *Config) error {
|
||||
if !cfg.Uprobes.ApacheEnabled {
|
||||
log.Printf("[uprobes] Apache uprobes désactivés (uprobes.apache_enabled=false)")
|
||||
return nil
|
||||
}
|
||||
maxRetries := cfg.Uprobes.MaxRetries
|
||||
retryInterval := time.Duration(cfg.Uprobes.RetryIntervalSec) * time.Second
|
||||
|
||||
|
||||
maxRetries := cfg.Uprobes.MaxRetries
|
||||
retryInterval := time.Duration(cfg.Uprobes.RetryIntervalSec) * time.Second
|
||||
|
||||
Reference in New Issue
Block a user