Événements ssh-guard (JSON)

Référence stable du flux d'événements émis par la couche ssh-guard du serveur SSH gitrust. Ce format est garanti stable pour les consommateurs externes (fail2ban, Loki, Vector, SIEM). Tout changement incompatible passe par un nouveau nom d'événement.

Où trouver ces événements : selon SSH_GUARD_LOG_TARGET, dans le journald du service (stderr), dans /var/log/gitrust-ssh-guard.json (file), ou les deux (both). Voir Variables d'environnement — SSH_GUARD_*.


1. Forme générale

Tous les événements partagent trois champs :

Champ Type Description
event string Nom snake_case du type d'événement (clé de filtrage).
ts string ISO 8601 UTC Horodatage UTC de l'événement (ex. 2026-04-19T14:32:11.482Z).
ip string Adresse IPv4 ou IPv6 réelle du client (après extraction PROXY si applicable).

Champs additionnels selon le type d'événement (voir tables ci-dessous).


2. Catalogue des événements

event Catégorie Émis par Cas d'usage admin
connection_accepted Trafic SecureListener Audit volume
connection_dropped Décision SecureListener ou ConnectionFloodDetector Volumétrie des refus
auth_failed Authentification AuthTracker Source principale fail2ban
auth_succeeded Authentification AuthTracker Audit accès légitimes
brute_force_detected Détection BruteForceDetector Signal fort fail2ban
user_enumeration_detected Détection UserEnumerationDetector Signal fort fail2ban
key_scanning_detected Détection KeyScanningDetector Signal fort fail2ban
connection_flood_detected Détection ConnectionFloodDetector Signal fort fail2ban
ip_banned Action BanManager Ban à appliquer côté firewall (fail2ban)
ip_unbanned Action BanManager Levée de ban (manuelle ou TTL)

3. Référence par événement

3.1 connection_accepted

Une connexion TCP a passé tous les contrôles ssh-guard. Le russh handshake va démarrer.

Champ Type Description
event "connection_accepted"
ts string Horodatage UTC
session_id string UUID v4 Identifiant unique de session, présent ensuite dans tous les événements liés (auth_failed, auth_succeeded)
ip string IP cliente réelle
{"event":"connection_accepted","ts":"2026-04-19T14:32:11.482Z","session_id":"a4c2b8e1-9f3d-4d7e-8c11-0a5d9b6f4e22","ip":"203.0.113.42"}

3.2 connection_dropped

ssh-guard a refusé la connexion avant le handshake SSH.

Champ Type Description
event "connection_dropped"
ts string Horodatage UTC
ip string IP source (réelle si PROXY parsé, sinon peer_addr)
reason enum string Voir tableau des raisons ci-dessous

Valeurs de reason :

reason Signification
banned IP couverte par un ban actif (auto ou denylist admin)
flood_limit Cap de connexions/seconde par IP atteint
proxy_header_missing PROXY protocol obligatoire mais en-tête absent (timeout)
untrusted_proxy En-tête PROXY reçu d'un socket pas dans trusted_proxies
proxy_header_invalid En-tête PROXY malformé ou version non autorisée
concurrent_limit Limite de sessions concurrentes par IP atteinte (placeholder, non actif)
{"event":"connection_dropped","ts":"2026-04-19T14:33:02.117Z","ip":"203.0.113.42","reason":"banned"}

3.3 auth_failed

Tentative d'authentification SSH refusée. Source principale pour fail2ban.

Champ Type Description
event "auth_failed"
ts string Horodatage UTC
session_id string UUID Lien avec le connection_accepted
ip string IP cliente réelle
user string ou null Nom d'utilisateur tenté (si fourni par le client)
method enum string Méthode SSH : none, password, public_key, keyboard_interactive, host_based
fingerprint string ou null Fingerprint SHA256 de la clé publique tentée (forme SHA256:...) ou null si non public_key
{"event":"auth_failed","ts":"2026-04-19T14:32:13.221Z","session_id":"a4c2b8e1-9f3d-4d7e-8c11-0a5d9b6f4e22","ip":"203.0.113.42","user":"root","method":"public_key","fingerprint":"SHA256:k1Qp9xJ8r6Z3HfV2Bn7tT5Cw"}

3.4 auth_succeeded

Tentative d'authentification SSH validée. Audit des accès légitimes.

Champ Type Description
event "auth_succeeded"
ts string Horodatage UTC
session_id string UUID
ip string
user string Nom d'utilisateur authentifié
fingerprint string ou null Fingerprint de la clé utilisée (peut être null si méthode sans clé)
{"event":"auth_succeeded","ts":"2026-04-19T14:32:14.005Z","session_id":"a4c2b8e1-9f3d-4d7e-8c11-0a5d9b6f4e22","ip":"203.0.113.42","user":"alice","fingerprint":"SHA256:p3Lm7nB6xQz9fK1WyT8c"}

3.5 brute_force_detected

Le seuil SSH_GUARD_BRUTE_FORCE_THRESHOLD a été atteint. Un événement ip_banned suit immédiatement.

Champ Type Description
event "brute_force_detected"
ts string Horodatage UTC
ip string IP fautive
count number Nombre d'auth_failed comptés dans la fenêtre
window_secs number Largeur de la fenêtre (en secondes)
{"event":"brute_force_detected","ts":"2026-04-19T14:36:42.998Z","ip":"203.0.113.42","count":5,"window_secs":300}

3.6 user_enumeration_detected

Le seuil SSH_GUARD_USER_ENUM_THRESHOLD (nombre d'usernames distincts essayés depuis la même IP) a été atteint.

Champ Type Description
event "user_enumeration_detected"
ts string Horodatage UTC
ip string
distinct_users number Nombre d'usernames distincts dans la fenêtre
window_secs number
{"event":"user_enumeration_detected","ts":"2026-04-19T14:38:15.402Z","ip":"203.0.113.42","distinct_users":10,"window_secs":300}

3.7 key_scanning_detected

Le seuil SSH_GUARD_KEY_SCAN_THRESHOLD (nombre de fingerprints distincts essayés depuis la même IP) a été atteint.

Champ Type Description
event "key_scanning_detected"
ts string Horodatage UTC
ip string
distinct_keys number Nombre de fingerprints distincts dans la fenêtre
window_secs number
{"event":"key_scanning_detected","ts":"2026-04-19T14:39:08.117Z","ip":"203.0.113.42","distinct_keys":10,"window_secs":300}

3.8 connection_flood_detected

Le cap SSH_GUARD_CONN_FLOOD_PER_SEC a été dépassé pour cette IP. Un connection_dropped avec reason="flood_limit" suit dans le même millième de seconde.

Champ Type Description
event "connection_flood_detected"
ts string Horodatage UTC
ip string
rate_per_sec number Cap nominal (valeur de SSH_GUARD_CONN_FLOOD_PER_SEC)
{"event":"connection_flood_detected","ts":"2026-04-19T14:40:01.555Z","ip":"203.0.113.42","rate_per_sec":10}

3.9 ip_banned

Un ban a été appliqué (auto par un détecteur, manuel par un admin, ou simulé en dry_run).

Champ Type Description
event "ip_banned"
ts string Horodatage UTC
ip string IP bannie (pour un CIDR > /32, l'adresse réseau)
reason enum string Voir tableau ci-dessous
expires_at string ou null Horodatage UTC d'expiration. null = ban permanent

Valeurs de reason :

reason Origine
brute_force BruteForceDetector
user_enumeration UserEnumerationDetector
key_scanning KeyScanningDetector
connection_flood ConnectionFloodDetector (rare : le flood drop ne pose pas de ban persistant par défaut)
admin_deny_list Ajout admin dans l'ACL deny
manual Ban manuel via UI/API admin
{"event":"ip_banned","ts":"2026-04-19T14:36:42.999Z","ip":"203.0.113.42","reason":"brute_force","expires_at":"2026-04-19T15:36:42.999Z"}

Mode dry_run : un ip_banned est émis pour signal externe (fail2ban) sans que ssh-guard inscrive le ban dans son store. Utile pour observer un nouveau seuil sans risque.

3.10 ip_unbanned

Un ban a été levé (TTL expiré ou action admin).

Champ Type Description
event "ip_unbanned"
ts string Horodatage UTC
ip string
manual boolean true = action admin, false = expiration auto
{"event":"ip_unbanned","ts":"2026-04-19T15:36:43.001Z","ip":"203.0.113.42","manual":false}

4. Filtrage côté outils externes

4.1 Fail2ban (filtre par regex sur le fichier JSON)

[Definition]
# Brute-force détecté → ban dur côté firewall
failregex = ^.*"event":"auth_failed".*"ip":"<HOST>".*$
ignoreregex =

Voir le template clé en main : template/security/fail2ban-gitrust-ssh-guard.conf et le how-to Durcir avec Fail2ban.

4.2 Loki (LogQL)

# Toutes les détections fortes (signal pour alerting Grafana)
{job="gitrust-ssh-guard"}
  | json
  | event=~"brute_force_detected|user_enumeration_detected|key_scanning_detected|connection_flood_detected"

4.3 jq (extraction shell)

# 10 IP les plus actives en échec d'auth sur la dernière heure
sudo journalctl -u gitrust --since "1 hour ago" --no-pager \
  | jq -r 'select(.event=="auth_failed") | .ip' \
  | sort | uniq -c | sort -rn | head -10

5. Pour aller plus loin