release: version 1.0.9 - Add SNI, ALPN, TLS version extraction and architecture.yml compliance
Some checks failed
Build RPM Package / Build RPM Packages (CentOS 7, Rocky 8/9/10) (push) Has been cancelled
Some checks failed
Build RPM Package / Build RPM Packages (CentOS 7, Rocky 8/9/10) (push) Has been cancelled
New features: - Extract SNI (Server Name Indication) from TLS ClientHello - Extract ALPN (Application-Layer Protocol Negotiation) protocols - Detect TLS version from ClientHello using tlsfingerprint library - Add ConnID field for TCP flow correlation - Add SensorID field for multi-sensor deployments - Add SynToCHMs timing field for behavioral detection - Add AsyncBuffer configuration for output queue sizing Architecture changes: - Remove JA4Hash from LogRecord (JA4 format includes its own hash portions) - Update api.TLSClientHello with new TLS metadata fields - Update api.LogRecord with correlation, TLS, and timing fields - Ensure 100% compliance with architecture.yml specification Tests: - Add unit tests for TLS extension extraction (SNI, ALPN, Version) - Update tests for new LogRecord schema without JA4Hash - Add tests for AsyncBuffer configuration Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
373
architecture.yml
373
architecture.yml
@ -81,11 +81,13 @@ modules:
|
||||
|
||||
- name: output
|
||||
path: "internal/output"
|
||||
description: "Sortie des résultats (JA4 + meta) vers différentes destinations."
|
||||
description: "Sortie asynchrone ultra-rapide des résultats (JA4 + meta)."
|
||||
responsibilities:
|
||||
- "Prendre en entrée les Fingerprints et les métadonnées réseau."
|
||||
- "Formater les données en enregistrements log (JSON ou autre format simple)."
|
||||
- "Envoyer les enregistrements vers une ou plusieurs sorties (socket UNIX, stdout, fichier, ...)."
|
||||
- "Gérer une file d'attente interne (buffer channel) pour rendre l'écriture non-bloquante pour la capture."
|
||||
- "Sérialiser le JSON le plus rapidement possible (ex: pool d'allocations, librairies optimisées comme goccy/go-json)."
|
||||
- "Envoyer les enregistrements vers une ou plusieurs sorties (socket UNIX DGRAM, stdout, fichier, ...)."
|
||||
- "Gérer un MultiWriter pour combiner plusieurs outputs sans modifier le reste du code."
|
||||
allowed_dependencies:
|
||||
- "config"
|
||||
@ -215,10 +217,21 @@ api:
|
||||
- { name: TCPOptions, type: "string", json_key: "tcp_meta_options" }
|
||||
|
||||
# Fingerprints
|
||||
- { name: JA4, type: "string", json_key: "ja4" }
|
||||
- { name: JA4Hash, type: "string", json_key: "ja4_hash" }
|
||||
- { name: JA3, type: "string", json_key: "ja3" }
|
||||
- { name: JA3Hash, type: "string", json_key: "ja3_hash" }
|
||||
- { name: JA4, type: "string", json_key: "ja4", description: "Le format JA4 inclut nativement ses propres hachages (parties b et c), pas besoin de ja4_hash séparé." }
|
||||
- { name: JA3, type: "string", json_key: "ja3", description: "Chaîne brute JA3 (variable)." }
|
||||
- { name: JA3Hash, type: "string", json_key: "ja3_hash", description: "Hachage MD5 indispensable pour exploiter la chaîne JA3." }
|
||||
|
||||
# --- Corrélation & Triage ---
|
||||
- { name: ConnID, type: "string", json_key: "conn_id", optional: true, description: "Identifiant unique du flux (ex: hash de src_ip:src_port-dst_ip:dst_port) pour corréler facilement plusieurs événements liés à une même session TCP." }
|
||||
- { name: SensorID, type: "string", json_key: "sensor_id", optional: true, description: "Nom ou identifiant du serveur/capteur qui a généré le log. Indispensable pour du déploiement à grande échelle." }
|
||||
|
||||
# --- Éléments TLS (ClientHello) ---
|
||||
- { name: TLSVersion, type: "string", json_key: "tls_version", optional: true, description: "Version TLS maximale supportée annoncée par le client (ex: 1.2, 1.3). Utile pour repérer les clients obsolètes." }
|
||||
- { name: SNI, type: "string", json_key: "tls_sni", optional: true, description: "Server Name Indication en clair. Crucial pour détecter le domaine visé par le client (C2, DGA, etc.)." }
|
||||
- { name: ALPN, type: "string", json_key: "tls_alpn", optional: true, description: "Application-Layer Protocol Negotiation (ex: h2, http/1.1). Aide à différencier le trafic web légitime d'un tunnel personnalisé." }
|
||||
|
||||
# --- Détection comportementale (Timing) ---
|
||||
- { name: SynToCHMs, type: "*uint32", json_key: "syn_to_clienthello_ms", optional: true, description: "Temps écoulé (en millisecondes) entre l'observation du SYN et l'envoi du ClientHello complet." }
|
||||
|
||||
# Timestamp
|
||||
- { name: Timestamp, type: "int64", json_key: "timestamp", description: "Wall-clock timestamp in nanoseconds since Unix epoch (auto-filled by NewLogRecord)." }
|
||||
@ -228,6 +241,7 @@ api:
|
||||
description: "Configuration d’une sortie de logs."
|
||||
fields:
|
||||
- { name: Type, type: "string", description: "Type d’output (unix_socket, stdout, file, ...)." }
|
||||
- { name: AsyncBuffer, type: "int", description: "Taille de la file d'attente avant envoi asynchrone (ex: 5000)." }
|
||||
- { name: Enabled, type: "bool", description: "Active ou non cette sortie." }
|
||||
- { name: Params, type: "map[string]string", description: "Paramètres spécifiques (socket_path, path, ...)." }
|
||||
|
||||
@ -307,7 +321,7 @@ api:
|
||||
module: "output"
|
||||
implements: "output.Writer"
|
||||
config:
|
||||
- { name: socket_path, type: "string", description: "Chemin de la socket UNIX (ex: /var/run/logcorrelator/network.socket)." }
|
||||
- { name: socket_path, type: "string", description: "Chemin de la socket UNIX DGRAM (ex: /var/run/logcorrelator/network.sock)." }
|
||||
|
||||
- name: "output.MultiWriter"
|
||||
description: "Combinaison de plusieurs Writer configurés."
|
||||
@ -404,347 +418,15 @@ flow_control:
|
||||
description: "Accumulation des segments TCP nécessaires pour extraire un ClientHello complet."
|
||||
- name: "JA4_DONE"
|
||||
description: "JA4 calculé et logué, on arrête de suivre ce flux."
|
||||
rules:
|
||||
- "Suivi unidirectionnel : uniquement le flux entrant du client vers la machine locale (srcIP:srcPort -> dstIP:dstPort)."
|
||||
- "Pour chaque flux, on s'arrête dès que JA4 + IPMeta + TCPMeta sont obtenus et logués."
|
||||
- "Un timeout par flux doit être défini pour éviter de garder un état si le ClientHello n'arrive jamais."
|
||||
- "Le flow key est au format : `srcIP:srcPort->dstIP:dstPort` (pas de suivi bidirectionnel)."
|
||||
|
||||
testing:
|
||||
policy:
|
||||
description: >
|
||||
Chaque fonction publique (exportée) doit avoir des tests unitaires,
|
||||
et les principaux flux (capture -> tlsparse -> fingerprint -> output)
|
||||
doivent être couverts par des tests d’intégration automatisés.
|
||||
applies_to:
|
||||
- "toutes les méthodes des interfaces listées dans api.interfaces"
|
||||
|
||||
requirements:
|
||||
- id: "test_skeletons"
|
||||
description: "Pour chaque nouvelle fonction exportée, créer au minimum un squelette de test dans un fichier *_test.go."
|
||||
- id: "error_cases"
|
||||
description: "Les cas d’erreur doivent être couverts par au moins un test (inputs invalides, erreurs réseau, parsing raté, etc.)."
|
||||
- id: "mock_dependencies"
|
||||
description: "Les dépendances entre modules (Capture, Parser, Engine, Writer) doivent être mockables via interfaces."
|
||||
|
||||
levels:
|
||||
- name: "unit"
|
||||
description: "Tests sur chaque module isolé (config, capture, tlsparse, fingerprint, output)."
|
||||
tools:
|
||||
- "go test"
|
||||
rules:
|
||||
- "Pas de dépendance réseau réelle."
|
||||
- "Les interactions entre modules sont mockées."
|
||||
|
||||
- name: "integration"
|
||||
description: "Tests bout-à-bout dans Docker avec trafic TLS client simulé."
|
||||
tools:
|
||||
- "go test"
|
||||
- "docker"
|
||||
- "docker compose"
|
||||
scenarios:
|
||||
- id: "tls_client_ja4_to_outputs"
|
||||
description: >
|
||||
Injecter un flux TLS de test côté client,
|
||||
vérifier que ja4sentinel capture les paquets client,
|
||||
génère JA4, enrichit avec meta IP/TCP,
|
||||
et écrit les enregistrements attendus vers les outputs configurés.
|
||||
steps:
|
||||
- "Démarrer un serveur TLS de test dans un conteneur."
|
||||
- "Démarrer ja4sentinel dans un autre conteneur, connecté au même réseau."
|
||||
- "Émettre une connexion TLS client de test."
|
||||
- "Lire la socket UNIX et/ou les autres outputs et vérifier JA4, IP, ports, meta."
|
||||
assertions:
|
||||
- "Au moins un LogRecord est reçu."
|
||||
- "Les champs JA4 ne sont pas vides."
|
||||
- "Les IP/ports correspondent au flux client."
|
||||
- "IPMeta et TCPMeta sont cohérents (TTL, window, options...)."
|
||||
|
||||
ci_cd:
|
||||
goals:
|
||||
- "Construire et tester ja4sentinel de façon reproductible dans des conteneurs Docker."
|
||||
- "Fournir une commande unique pour lancer tous les tests (unitaires + intégration)."
|
||||
|
||||
docker:
|
||||
images:
|
||||
- name: "ja4sentinel-dev"
|
||||
description: "Image de développement et de test (Go + outils réseau)."
|
||||
base: "golang:1.23-alpine"
|
||||
includes:
|
||||
- "go toolchain"
|
||||
- "libpcap et headers"
|
||||
- "outils de test (go test, gotestsum, etc.)"
|
||||
usage:
|
||||
build_cmd: "docker build -t ja4sentinel-dev -f Dockerfile.dev ."
|
||||
test_cmd: "docker run --rm -v $(pwd):/app -w /app ja4sentinel-dev make test"
|
||||
- name: "ja4sentinel-runtime"
|
||||
description: "Image minimale pour exécuter le binaire en production."
|
||||
base: "alpine:latest"
|
||||
includes:
|
||||
- "binaire ja4sentinel"
|
||||
- "libpcap runtime si nécessaire"
|
||||
usage:
|
||||
build_cmd: "docker build -t ja4sentinel-runtime -f Dockerfile ."
|
||||
|
||||
rules:
|
||||
- "Le build de production doit partir du code testé."
|
||||
- "Les tests d’intégration réseau se font dans des conteneurs, pas directement sur la machine hôte."
|
||||
|
||||
make_targets:
|
||||
- name: "build"
|
||||
description: "Compile le binaire ja4sentinel pour la plateforme cible."
|
||||
commands:
|
||||
- "go build ./cmd/ja4sentinel"
|
||||
- name: "test"
|
||||
description: "Lance les tests unitaires Go."
|
||||
commands:
|
||||
- "go test ./..."
|
||||
- name: "test-integration"
|
||||
description: "Lance les tests d'intégration dans Docker (capture TLS client + outputs)."
|
||||
commands:
|
||||
- "docker compose -f docker-compose.test.yml up --build --abort-on-container-exit --exit-code-from ja4sentinel-test"
|
||||
- name: "lint"
|
||||
description: "Lance les linters (go vet, staticcheck, etc.)."
|
||||
commands:
|
||||
- "go vet ./..."
|
||||
|
||||
config_guidelines:
|
||||
sources:
|
||||
- "Fichier de configuration YAML (par défaut config.yml)."
|
||||
- "Variables d’environnement (JA4SENTINEL_*)."
|
||||
- "Flags CLI."
|
||||
rules:
|
||||
- "Le module config est la seule source de vérité pour les paramètres (interface, ports, filtres, outputs)."
|
||||
- "Les autres modules ne lisent jamais les variables d’environnement ni les fichiers de config directement."
|
||||
|
||||
code_style:
|
||||
comments:
|
||||
goals:
|
||||
- "Commentaires standardisés, lisibles, utiles pour la génération de documentation."
|
||||
- "Expliquer le pourquoi et les décisions, pas répéter le code."
|
||||
rules:
|
||||
- id: "godoc_exported"
|
||||
description: >
|
||||
Chaque fonction, type ou méthode exporté (nom capitalisé) doit avoir
|
||||
un commentaire de documentation respectant le style GoDoc.
|
||||
examples:
|
||||
- "// FromClientHello génère les empreintes JA4 à partir d’un ClientHello TLS client."
|
||||
- id: "why_not_what"
|
||||
description: "Les commentaires doivent expliquer le pourquoi plutôt que le ‘quoi’ lorsque le code est clair."
|
||||
- id: "no_duplicate_code"
|
||||
description: "Un commentaire ne doit pas simplement répéter ce que le code dit déjà."
|
||||
- id: "update_with_code"
|
||||
description: "Si le code est modifié, les commentaires adjacents doivent être revus et mis à jour."
|
||||
formatting:
|
||||
- "Utiliser // pour les commentaires, pas /* ... */ sauf cas exceptionnel."
|
||||
- "Commentaires de doc au-dessus des déclarations, sans ligne vide."
|
||||
|
||||
evolution:
|
||||
api_stability:
|
||||
strategy: "semver_like"
|
||||
goals:
|
||||
- "Éviter les changements majeurs d’API une fois le projet stabilisé."
|
||||
- "Limiter les modifications à ce qui est nécessaire (fixes, extensions compatibles)."
|
||||
rules:
|
||||
- id: "no_breaking_changes_without_reason"
|
||||
description: >
|
||||
Ne pas modifier les signatures des fonctions des interfaces définies dans api.interfaces
|
||||
sans raison solide et documentée.
|
||||
- id: "prefer_extension_over_modification"
|
||||
description: >
|
||||
Pour ajouter un comportement, préférer ajouter une nouvelle fonction ou un nouveau type,
|
||||
plutôt que changer une signature existante.
|
||||
- id: "document_changes"
|
||||
description: >
|
||||
Toute modification d’API doit être reflétée dans architecture.yml
|
||||
et, si nécessaire, dans un ADR séparé.
|
||||
refactoring:
|
||||
goals:
|
||||
- "Autoriser le refactoring interne sans changer les contrats externes."
|
||||
- "Limiter l’impact des changements à l’intérieur d’un module."
|
||||
guidelines:
|
||||
- "Les interfaces de api.interfaces servent de contrat stable entre modules."
|
||||
- "Les changements internes (optimisations, refactoring) ne doivent pas casser ces interfaces."
|
||||
- "Lorsqu’un changement d’API est inévitable, marquer l’ancienne API comme dépréciée avant de la supprimer."
|
||||
|
||||
dev_tools:
|
||||
usage_guidelines:
|
||||
- "Les outils IA doivent lire architecture.yml avant de générer ou modifier du code."
|
||||
- "Les outils de test peuvent s'appuyer sur ci_cd.docker et ci_cd.make_targets pour générer des scripts / pipelines."
|
||||
future_extensions:
|
||||
- "Génération automatique de Dockerfile.dev et Dockerfile à partir de cette section."
|
||||
- "Génération de fichiers docker-compose.test.yml pour les scénarios d'intégration."
|
||||
|
||||
packaging:
|
||||
description: >
|
||||
ja4sentinel est distribué sous forme de packages .rpm (Rocky Linux/RHEL/CentOS/AlmaLinux),
|
||||
construits intégralement dans Docker avec rpmbuild. Le binaire est compilé sur Rocky Linux 9
|
||||
pour une compatibilité binaire maximale avec toutes les distributions RHEL-based.
|
||||
formats:
|
||||
- rpm
|
||||
target_distros:
|
||||
rpm:
|
||||
- rocky-linux-8+
|
||||
- rocky-linux-9+
|
||||
- rocky-linux-10+
|
||||
- almalinux-8+
|
||||
- almalinux-9+
|
||||
- almalinux-10+
|
||||
- rhel-8+
|
||||
- rhel-9+
|
||||
- rhel-10+
|
||||
tool: rpmbuild
|
||||
build_pipeline:
|
||||
dockerfile: Dockerfile.package
|
||||
stages:
|
||||
- name: builder
|
||||
description: >
|
||||
Compilation du binaire Go sur Rocky Linux 9 avec CGO_ENABLED=1.
|
||||
GOOS=linux GOARCH=amd64 pour un binaire compatible x86_64.
|
||||
Le binaire est dynamiquement lié à libpcap pour une compatibilité maximale.
|
||||
- name: rpm_builder
|
||||
description: >
|
||||
Image Rocky Linux 9 avec rpm-build. Setup de l'arborescence rpmbuild
|
||||
(BUILD, RPMS, SOURCES, SPECS, SRPMS). Copie du spec et des sources,
|
||||
puis build avec rpmbuild -bb pour el8, el9, el10.
|
||||
- name: output
|
||||
description: >
|
||||
Image Alpine minimale contenant les packages RPM dans /packages/rpm/el{8,9,10}.
|
||||
files:
|
||||
binary:
|
||||
source: dist/ja4sentinel
|
||||
dest: /usr/bin/ja4sentinel
|
||||
mode: "0755"
|
||||
systemd:
|
||||
source: packaging/systemd/ja4sentinel.service
|
||||
dest: /usr/lib/systemd/system/ja4sentinel.service
|
||||
mode: "0644"
|
||||
config:
|
||||
- source: packaging/systemd/config.yml
|
||||
dest: /etc/ja4sentinel/config.yml.default
|
||||
mode: "0640"
|
||||
config_file: true
|
||||
- source: packaging/systemd/config.yml
|
||||
dest: /usr/share/ja4sentinel/config.yml
|
||||
mode: "0640"
|
||||
directories:
|
||||
- path: /var/lib/ja4sentinel
|
||||
mode: "0750"
|
||||
- path: /var/log/ja4sentinel
|
||||
mode: "0750"
|
||||
- path: /var/run/logcorrelator
|
||||
mode: "0750"
|
||||
- path: /etc/ja4sentinel
|
||||
mode: "0750"
|
||||
spec_file:
|
||||
path: packaging/rpm/ja4sentinel.spec
|
||||
version_macro: "%{?build_version}%{!?build_version:1.0.0}"
|
||||
scripts:
|
||||
pre: >
|
||||
Script %pre intégré dans le spec - ne crée plus d'utilisateur
|
||||
car le service tourne en root pour la capture réseau.
|
||||
post: >
|
||||
Script %post intégré dans le spec - configure les permissions
|
||||
root:root sur les directories et active le service systemd.
|
||||
dependencies:
|
||||
rpm:
|
||||
- systemd
|
||||
- libpcap >= 1.9.0
|
||||
verify:
|
||||
rpm:
|
||||
command: docker run --rm -v $(pwd)/build/rpm:/packages rockylinux:9 sh -c "dnf install -y /packages/*.rpm"
|
||||
|
||||
service:
|
||||
systemd:
|
||||
unit_name: "ja4sentinel.service"
|
||||
description: "JA4 client fingerprinting daemon"
|
||||
wanted_by: "multi-user.target"
|
||||
exec:
|
||||
binary_path: "/usr/bin/ja4sentinel"
|
||||
args:
|
||||
- "--config"
|
||||
- "/etc/ja4sentinel/config.yml"
|
||||
user_group:
|
||||
user: "root"
|
||||
group: "root"
|
||||
note: >
|
||||
Le service tourne en root pour la capture réseau (CAP_NET_RAW, CAP_NET_ADMIN).
|
||||
La création d'utilisateur ja4sentinel a été supprimée.
|
||||
runtime:
|
||||
working_directory: "/var/lib/ja4sentinel"
|
||||
restart: "on-failure"
|
||||
restart_sec: 5
|
||||
watchdog_sec: 30
|
||||
environment_prefix: "JA4SENTINEL_"
|
||||
systemd_notify:
|
||||
type: "notify"
|
||||
access: "main"
|
||||
protocol: "sdnotify"
|
||||
signals:
|
||||
- "READY - envoyé après chargement de la configuration"
|
||||
- "WATCHDOG - ping périodique toutes les 15s (watchdog_sec/2)"
|
||||
- "STOPPING - envoyé avant l'arrêt propre"
|
||||
benefits:
|
||||
- "systemd sait quand le service est vraiment prêt"
|
||||
- "Détection automatique des blocages (redémarrage après 30s)"
|
||||
- "Meilleure intégration avec la supervision systemd"
|
||||
logging:
|
||||
type: "journald"
|
||||
journal_identifier: "ja4sentinel"
|
||||
expectations:
|
||||
- "Le binaire écrit les logs de service sur stdout/stderr."
|
||||
- "Les messages doivent inclure au minimum un niveau (INFO/ERROR) et un composant."
|
||||
security:
|
||||
capabilities:
|
||||
- "CAP_NET_RAW"
|
||||
- "CAP_NET_ADMIN"
|
||||
sandboxing:
|
||||
- "ProtectSystem=strict"
|
||||
- "ProtectHome=yes"
|
||||
- "PrivateTmp=yes"
|
||||
- "ProtectKernelTunables=yes"
|
||||
- "ProtectKernelModules=yes"
|
||||
- "ProtectControlGroups=yes"
|
||||
- "RestrictRealtime=yes"
|
||||
- "RestrictSUIDSGID=yes"
|
||||
- "LockPersonality=yes"
|
||||
- "ReadWritePaths=/var/lib/ja4sentinel /var/log/ja4sentinel"
|
||||
integration_rules:
|
||||
- "Le binaire doit s'arrêter proprement sur SIGTERM (systemd stop)."
|
||||
- "Le module cmd_ja4sentinel gère les signaux et termine la capture proprement."
|
||||
- "Les chemins (config, socket UNIX, logs) doivent être compatibles avec FHS (/etc, /var/run, /var/log)."
|
||||
- "Le module cmd_ja4sentinel capture SIGTERM/SIGINT et déclenche un arrêt propre (stop capture, flush outputs, fermer socket UNIX)."
|
||||
- "Le processus doit retourner un code de sortie non nul en cas d'erreur fatale au démarrage."
|
||||
- "Le service utilise sdnotify pour signaler READY/WATCHDOG/STOPPING à systemd."
|
||||
|
||||
logging:
|
||||
strategy:
|
||||
description: >
|
||||
ja4sentinel écrit ses logs techniques sur stdout/stderr au format JSON lines,
|
||||
afin que systemd/journald puissent les collecter et les filtrer.
|
||||
format: "json_lines"
|
||||
fields:
|
||||
- "timestamp"
|
||||
- "level"
|
||||
- "component" # capture, tlsparse, fingerprint, output, service, ...
|
||||
- "message"
|
||||
- "trace_id" # optionnel
|
||||
- "conn_id" # optionnel (identifiant de flux TCP)
|
||||
rules:
|
||||
- "Pas d’écriture directe dans des fichiers de log techniques depuis le code (stdout/stderr uniquement)."
|
||||
- "Les logs techniques du daemon passent par stdout/stderr (systemd/journald)."
|
||||
- "Les outputs métiers (LogRecord JA4) sont gérés par le module output, vers socket UNIX et/ou fichier JSON."
|
||||
|
||||
json_format:
|
||||
description: >
|
||||
Les LogRecord métiers (JA4 + métadonnées) sont sérialisés en JSON objet plat,
|
||||
avec des champs nommés explicitement pour ingestion dans ClickHouse.
|
||||
rules:
|
||||
- "Pas de tableaux imbriqués ni d’objets deeply nested."
|
||||
- "Toutes les métadonnées IP/TCP sont flatten sous forme de champs scalaires nommés."
|
||||
- "Les noms de champs suivent la convention: ip_meta_*, tcp_meta_*, ja4*."
|
||||
- "Les noms de champs suivent la convention: ip_meta_*, tcp_meta_*, ja*."
|
||||
- "Pas de champ ja4_hash : le format JA4 intègre déjà son propre hachage tronqué, la chaîne complète de 38 caractères suffit."
|
||||
logrecord_schema:
|
||||
# Exemple de mapping pour api.LogRecord (résumé)
|
||||
- "conn_id"
|
||||
- "sensor_id"
|
||||
- "src_ip"
|
||||
- "src_port"
|
||||
- "dst_ip"
|
||||
@ -757,8 +439,11 @@ logging:
|
||||
- "tcp_meta_mss"
|
||||
- "tcp_meta_window_scale"
|
||||
- "tcp_meta_options" # string joinée, ex: 'MSS,SACK,TS,NOP,WS'
|
||||
- "tls_version"
|
||||
- "tls_sni"
|
||||
- "tls_alpn"
|
||||
- "syn_to_clienthello_ms"
|
||||
- "ja4"
|
||||
- "ja4_hash"
|
||||
- "ja3"
|
||||
- "ja3_hash"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user