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_read` | uprobe + uretprobe | Capture les requêtes du client (direction=0) |
|
||||||
| `SSL_write` | uprobe + uretprobe | Capture les réponses du serveur (direction=1) |
|
| `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 :
|
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 |
|
| 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_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 |
|
| `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`.
|
**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é) :
|
**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)
|
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)
|
2. `accept_map[{pid_tgid, fd}]` — cache accept4 (tracepoint kernel)
|
||||||
|
|||||||
@ -65,8 +65,7 @@ type Config struct {
|
|||||||
|
|
||||||
Uprobes struct {
|
Uprobes struct {
|
||||||
Enabled bool `yaml:"enabled"` // activer l'attachement automatique des uprobes
|
Enabled bool `yaml:"enabled"` // activer l'attachement automatique des uprobes
|
||||||
NginxBinPath string `yaml:"nginx_bin_path"` // chemin vers le binaire nginx
|
Servers []string `yaml:"servers"` // serveurs web à capturer: ["nginx", "apache", "both"]
|
||||||
ApacheEnabled bool `yaml:"apache_enabled"` // activer la capture Apache httpd
|
|
||||||
MaxRetries int `yaml:"max_retries"` // nombre de tentatives d'attachement (défaut: 30)
|
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)
|
RetryIntervalSec int `yaml:"retry_interval_sec"` // intervalle entre tentatives (défaut: 2)
|
||||||
} `yaml:"uprobes"`
|
} `yaml:"uprobes"`
|
||||||
@ -89,8 +88,7 @@ func loadConfig(path string) (*Config, error) {
|
|||||||
cfg.Log.Level = "info"
|
cfg.Log.Level = "info"
|
||||||
cfg.Log.Format = "json"
|
cfg.Log.Format = "json"
|
||||||
cfg.Uprobes.Enabled = false
|
cfg.Uprobes.Enabled = false
|
||||||
cfg.Uprobes.NginxBinPath = "/usr/sbin/nginx"
|
cfg.Uprobes.Servers = []string{} // vide par défaut, peut être ["nginx"], ["apache"], ["nginx", "apache"]
|
||||||
cfg.Uprobes.ApacheEnabled = false
|
|
||||||
cfg.Uprobes.MaxRetries = 30
|
cfg.Uprobes.MaxRetries = 30
|
||||||
cfg.Uprobes.RetryIntervalSec = 2
|
cfg.Uprobes.RetryIntervalSec = 2
|
||||||
|
|
||||||
@ -140,11 +138,17 @@ func loadConfig(path string) (*Config, error) {
|
|||||||
if v := os.Getenv("JA4EBPF_UPROBES_ENABLED"); v != "" {
|
if v := os.Getenv("JA4EBPF_UPROBES_ENABLED"); v != "" {
|
||||||
cfg.Uprobes.Enabled = strings.EqualFold(v, "true") || v == "1" || v == "yes"
|
cfg.Uprobes.Enabled = strings.EqualFold(v, "true") || v == "1" || v == "yes"
|
||||||
}
|
}
|
||||||
if v := os.Getenv("JA4EBPF_NGINX_BIN_PATH"); v != "" {
|
if v := os.Getenv("JA4EBPF_UPROBES_SERVERS"); v != "" {
|
||||||
cfg.Uprobes.NginxBinPath = 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)
|
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 {
|
if err := attachNginxUprobesWithRetry(ctx, ldr, cfg); err != nil {
|
||||||
log.Printf("[ja4ebpf] erreur attachement uprobes nginx: %v", err)
|
log.Printf("[ja4ebpf] erreur attachement uprobes nginx: %v", err)
|
||||||
}
|
}
|
||||||
|
case "apache":
|
||||||
// --- 4c. Attachement uprobes Apache httpd ---
|
|
||||||
if cfg.Uprobes.ApacheEnabled {
|
|
||||||
if err := attachApacheUprobesWithRetry(ctx, ldr, cfg); err != nil {
|
if err := attachApacheUprobesWithRetry(ctx, ldr, cfg); err != nil {
|
||||||
log.Printf("[ja4ebpf] erreur attachement uprobes Apache: %v", err)
|
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) ---
|
// --- 8. Compteurs d'événements consommés (mode debug) ---
|
||||||
consumed := &eventCounters{}
|
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)
|
go consumeNginxHTTPEvents(ctx, ldr.NginxHTTPReader, mgr, &consumed.nginx)
|
||||||
if cfg.Uprobes.ApacheEnabled {
|
case "apache":
|
||||||
go consumeApacheHTTPEvents(ctx, ldr.ApacheHTTPReader, mgr, &consumed.apache)
|
go consumeApacheHTTPEvents(ctx, ldr.ApacheHTTPReader, mgr, &consumed.apache)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- 9. Goroutines de consommation des ring buffers ---
|
// --- 9. Goroutines de consommation des ring buffers ---
|
||||||
go consumeSynEvents(ctx, ldr.SynReader, mgr, &consumed.syn)
|
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.
|
// Retente jusqu'à maxRetries fois toutes les retryInterval secondes.
|
||||||
// Utile pour attendre que nginx démarre après ja4ebpf.
|
// Utile pour attendre que nginx démarre après ja4ebpf.
|
||||||
func attachNginxUprobesWithRetry(ctx context.Context, l *loader.Loader, cfg *Config) error {
|
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
|
maxRetries := cfg.Uprobes.MaxRetries
|
||||||
retryInterval := time.Duration(cfg.Uprobes.RetryIntervalSec) * time.Second
|
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)",
|
log.Printf("[uprobes] tentative d'attachement nginx uprobes (bin=%s, max_retries=%d, interval=%v)",
|
||||||
binPath, maxRetries, retryInterval)
|
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.
|
// Retente jusqu'à maxRetries fois toutes les retryInterval secondes.
|
||||||
// Utile pour attendre que Apache httpd démarre après ja4ebpf.
|
// Utile pour attendre que Apache httpd démarre après ja4ebpf.
|
||||||
func attachApacheUprobesWithRetry(ctx context.Context, l *loader.Loader, cfg *Config) error {
|
func attachApacheUprobesWithRetry(ctx context.Context, l *loader.Loader, cfg *Config) error {
|
||||||
if !cfg.Uprobes.ApacheEnabled {
|
maxRetries := cfg.Uprobes.MaxRetries
|
||||||
log.Printf("[uprobes] Apache uprobes désactivés (uprobes.apache_enabled=false)")
|
retryInterval := time.Duration(cfg.Uprobes.RetryIntervalSec) * time.Second
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
maxRetries := cfg.Uprobes.MaxRetries
|
maxRetries := cfg.Uprobes.MaxRetries
|
||||||
retryInterval := time.Duration(cfg.Uprobes.RetryIntervalSec) * time.Second
|
retryInterval := time.Duration(cfg.Uprobes.RetryIntervalSec) * time.Second
|
||||||
|
|||||||
Reference in New Issue
Block a user