Files
ja4sentinel/api/types.go
Jacquin Antoine 9280cb545c feat: version 1.0.0 avec corrections critiques et nommage de packages
Ajout du point d'entrée principal :
- cmd/ja4sentinel/main.go : pipeline complet avec gestion des signaux
- Intégration des modules (capture, tlsparse, fingerprint, output)
- Shutdown propre avec context.Context

Corrections du parsing TLS :
- Flow key unidirectionnel (client → serveur uniquement)
- Timeout de flux configurable via FlowTimeoutSec
- Structure ConnectionFlow simplifiée

Améliorations de l'API :
- Champs TCPMSS et TCPWScale en pointeurs (omitempty correct)
- NewLogRecord mis à jour pour les champs optionnels

Mise à jour de l'architecture :
- architecture.yml : documentation des champs optionnels
- Règles de flux unidirectionnel documentées

Système de packages :
- Version par défaut : 1.0.0
- Nommage cohérent : ja4sentinel_1.0.0_amd64.deb
- Scripts build-deb.sh et build-rpm.sh simplifiés
- Extraction correcte des checksums

Tests :
- TestFlowKey mis à jour pour le format unidirectionnel
- Tous les tests passent (go test ./...)
- go vet clean

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-02-26 23:24:42 +01:00

239 lines
6.6 KiB
Go

package api
// ServiceLog represents internal service logging for diagnostics
type ServiceLog struct {
Level string `json:"level"`
Component string `json:"component"`
Message string `json:"message"`
Details map[string]string `json:"details,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"` // Unix nanoseconds (auto-set by logger)
TraceID string `json:"trace_id,omitempty"` // Optional distributed tracing ID
ConnID string `json:"conn_id,omitempty"` // Optional TCP flow identifier
}
// Config holds basic network and TLS configuration
type Config struct {
Interface string `json:"interface"`
ListenPorts []uint16 `json:"listen_ports"`
BPFFilter string `json:"bpf_filter,omitempty"`
FlowTimeoutSec int `json:"flow_timeout_sec,omitempty"` // Timeout for TLS handshake extraction (default: 30)
}
// IPMeta contains IP metadata for stack fingerprinting
type IPMeta struct {
TTL uint8 `json:"ttl"`
TotalLength uint16 `json:"total_length"`
IPID uint16 `json:"id"`
DF bool `json:"df"`
}
// TCPMeta contains TCP metadata for stack fingerprinting
type TCPMeta struct {
WindowSize uint16 `json:"window_size"`
MSS uint16 `json:"mss,omitempty"`
WindowScale uint8 `json:"window_scale,omitempty"`
Options []string `json:"options"`
}
// RawPacket represents a raw packet captured from the network
type RawPacket struct {
Data []byte `json:"-"` // Not serialized
Timestamp int64 `json:"timestamp"` // nanoseconds since epoch
}
// TLSClientHello represents a client-side TLS ClientHello with IP/TCP metadata
type TLSClientHello struct {
SrcIP string `json:"src_ip"`
SrcPort uint16 `json:"src_port"`
DstIP string `json:"dst_ip"`
DstPort uint16 `json:"dst_port"`
Payload []byte `json:"-"` // Not serialized
IPMeta IPMeta `json:"ip_meta"`
TCPMeta TCPMeta `json:"tcp_meta"`
}
// Fingerprints contains TLS fingerprints for a client flow
type Fingerprints struct {
JA4 string `json:"ja4"`
JA4Hash string `json:"ja4_hash"`
JA3 string `json:"ja3,omitempty"`
JA3Hash string `json:"ja3_hash,omitempty"`
}
// LogRecord is the final log record, serialized as a flat JSON object
type LogRecord struct {
SrcIP string `json:"src_ip"`
SrcPort uint16 `json:"src_port"`
DstIP string `json:"dst_ip"`
DstPort uint16 `json:"dst_port"`
// Flattened IPMeta fields
IPTTL uint8 `json:"ip_meta_ttl"`
IPTotalLen uint16 `json:"ip_meta_total_length"`
IPID uint16 `json:"ip_meta_id"`
IPDF bool `json:"ip_meta_df"`
// Flattened TCPMeta fields
TCPWindow uint16 `json:"tcp_meta_window_size"`
TCPMSS *uint16 `json:"tcp_meta_mss,omitempty"`
TCPWScale *uint8 `json:"tcp_meta_window_scale,omitempty"`
TCPOptions string `json:"tcp_meta_options"` // comma-separated list
// Fingerprints
JA4 string `json:"ja4"`
JA4Hash string `json:"ja4_hash"`
JA3 string `json:"ja3,omitempty"`
JA3Hash string `json:"ja3_hash,omitempty"`
}
// OutputConfig defines configuration for a single log output
type OutputConfig struct {
Type string `json:"type"` // unix_socket, stdout, file, etc.
Enabled bool `json:"enabled"` // whether this output is active
Params map[string]string `json:"params"` // specific parameters like socket_path, path, etc.
}
// AppConfig is the complete ja4sentinel configuration
type AppConfig struct {
Core Config `json:"core"`
Outputs []OutputConfig `json:"outputs"`
}
// Loader interface loads configuration from file/env/CLI
type Loader interface {
Load() (AppConfig, error)
}
// Capture interface provides raw network packets
type Capture interface {
Run(cfg Config, out chan<- RawPacket) error
Close() error
}
// Parser converts RawPacket to TLSClientHello
type Parser interface {
Process(pkt RawPacket) (*TLSClientHello, error)
Close() error
}
// Engine generates JA4 fingerprints from TLS ClientHello
type Engine interface {
FromClientHello(ch TLSClientHello) (*Fingerprints, error)
}
// Writer is the generic interface for writing results
type Writer interface {
Write(rec LogRecord) error
}
// UnixSocketWriter implements Writer sending logs to a UNIX socket
type UnixSocketWriter interface {
Writer
Close() error
}
// MultiWriter combines multiple Writers
type MultiWriter interface {
Writer
Add(writer Writer)
CloseAll() error
}
// Builder constructs Writers from AppConfig
type Builder interface {
NewFromConfig(cfg AppConfig) (Writer, error)
}
// Logger interface for service logging
type Logger interface {
Debug(component, message string, details map[string]string)
Info(component, message string, details map[string]string)
Warn(component, message string, details map[string]string)
Error(component, message string, details map[string]string)
}
// Helper functions for creating and converting records
// NewLogRecord creates a LogRecord from TLSClientHello and Fingerprints
func NewLogRecord(ch TLSClientHello, fp *Fingerprints) LogRecord {
opts := ""
if len(ch.TCPMeta.Options) > 0 {
opts = joinStringSlice(ch.TCPMeta.Options, ",")
}
// Helper to create pointer from value for optional fields
var mssPtr *uint16
if ch.TCPMeta.MSS != 0 {
mssPtr = &ch.TCPMeta.MSS
}
var wScalePtr *uint8
if ch.TCPMeta.WindowScale != 0 {
wScalePtr = &ch.TCPMeta.WindowScale
}
rec := LogRecord{
SrcIP: ch.SrcIP,
SrcPort: ch.SrcPort,
DstIP: ch.DstIP,
DstPort: ch.DstPort,
IPTTL: ch.IPMeta.TTL,
IPTotalLen: ch.IPMeta.TotalLength,
IPID: ch.IPMeta.IPID,
IPDF: ch.IPMeta.DF,
TCPWindow: ch.TCPMeta.WindowSize,
TCPMSS: mssPtr,
TCPWScale: wScalePtr,
TCPOptions: opts,
}
if fp != nil {
rec.JA4 = fp.JA4
rec.JA4Hash = fp.JA4Hash
rec.JA3 = fp.JA3
rec.JA3Hash = fp.JA3Hash
}
return rec
}
// Helper to join string slice with separator
func joinStringSlice(slice []string, sep string) string {
if len(slice) == 0 {
return ""
}
result := slice[0]
for _, s := range slice[1:] {
result += sep + s
}
return result
}
// Default values and constants
const (
DefaultInterface = "eth0"
DefaultPort = 443
DefaultBPFFilter = ""
DefaultFlowTimeout = 30 // seconds
// Logging levels
LogLevelDebug = "DEBUG"
LogLevelInfo = "INFO"
LogLevelWarn = "WARN"
LogLevelError = "ERROR"
)
// DefaultConfig returns a configuration with sensible defaults
func DefaultConfig() AppConfig {
return AppConfig{
Core: Config{
Interface: DefaultInterface,
ListenPorts: []uint16{DefaultPort},
BPFFilter: DefaultBPFFilter,
FlowTimeoutSec: DefaultFlowTimeout,
},
Outputs: []OutputConfig{},
}
}