Objectifs du Projet
Ce projet fait directement suite au projet DevSecOps, dans lequel le honeypot HTTP était déjà en place mais muet : il ne loggait rien, et aucune alerte ne remontait nulle part. L’objectif ici était d’aller jusqu’au bout — instrumenter chaque leurre, construire le pipeline complet vers un SIEM, et avoir une visibilité réelle sur ce qui arrive sur le serveur.
Les critères que je me suis fixés :
- Détection active : chaque connexion suspecte déclenche une alerte dans le SIEM, avec les données utiles (IP source, credentials tentés, commandes exécutées).
- Couverture complète : SSH, HTTP, et le reverse proxy lui-même sont tous surveillés.
- Production-ready : tout est déployable en une commande via Ansible, sur un vrai VPS, sans intervention manuelle.
Architecture

Deux réseaux Docker distincts assurent l’isolation : public_net pour les services exposés, trap_net interne uniquement pour les honeypots. Les leurres ne sont jamais joignables directement depuis Internet — seul Traefik peut les atteindre.
L’agent Wazuh tourne directement sur le host et lit les fichiers de logs via les volumes Docker nommés. Les logs remontent au manager Wazuh (lui-même en Docker) sur localhost.
Honeypot SSH — Cowrie
Cowrie est un honeypot SSH medium-interaction : il accepte n’importe quelle combinaison identifiant/mot de passe et présente un faux shell. L’attaquant croit être connecté à un vrai serveur, pendant que chaque commande est enregistrée.
Le vrai SSH admin est déplacé sur le port 2222 (configuré par Ansible). Le port 22 public est entièrement capté par Cowrie.
cowrie:
image: cowrie/cowrie:latest
ports:
- "22:2222" # Port 22 public → Cowrie (honeypot)
volumes:
- cowrie_logs:/cowrie/cowrie-git/var/log/cowrie
Voici des alertes réelles remontées dans Wazuh lors des tests :
Tentative de connexion SSH (règle 100201 — level 10) :
{
"timestamp": "2026-04-03T13:10:48.792+0000",
"rule": { "level": 10, "description": "Cowrie: SSH brute-force attempt", "id": "100201" },
"data": {
"eventid": "cowrie.login.failed",
"src_ip": "5.5.5.5",
"username": "root",
"password": "toor"
}
}
Connexion réussie au leurre (règle 100202 — level 14) :
{
"timestamp": "2026-04-03T13:16:18.829+0000",
"rule": { "level": 14, "description": "Cowrie: Attacker authenticated to SSH honeypot", "id": "100202" },
"data": {
"eventid": "cowrie.login.success",
"src_ip": "192.168.1.114",
"username": "root",
"password": "test"
}
}
Commande exécutée dans le faux shell (règle 100203 — level 12) :
{
"timestamp": "2026-04-03T13:18:24.841+0000",
"rule": { "level": 12, "description": "Cowrie: Command executed in SSH honeypot", "id": "100203" },
"data": {
"eventid": "cowrie.command.input",
"src_ip": "192.168.1.114",
"input": "exit"
}
}
Honeypot HTTP — Flask
Le honeypot HTTP simule une interface WordPress : page de connexion wp-admin complète, avec logo, formulaire et comportement attendu par les bots. Contrairement à une réponse nginx statique, Flask capture tout ce qui est soumis.
Chaque requête est loggée en JSON avec les credentials en clair. Alerte réelle remontée dans Wazuh (règle 100102 — level 12) :
{
"timestamp": "2026-04-03T17:42:45.830+0000",
"rule": { "level": 12, "description": "HTTP Honeypot: credentials soumis au leurre", "id": "100102" },
"data": {
"src_ip": "10.10.10.3",
"http_method": "POST",
"http_path": "/wp-login.php",
"user_agent": "curl/8.14.1",
"credentials": {
"log": "admin",
"pwd": "password123"
}
}
}
Le routage Traefik dirige uniquement /wp-admin et /wp-login.php vers ce service. Tout le reste arrive sur le portfolio.
labels:
- "traefik.http.routers.honeypot.rule=PathPrefix(`/wp-admin`) || PathPrefix(`/wp-login.php`)"
- "traefik.http.routers.honeypot.priority=10"
La priorité élevée garantit que ces routes sont interceptées avant la règle catch-all du portfolio.
SIEM — Wazuh
Wazuh est déployé en mode single-node via Docker (manager + indexer + dashboard). L’agent sur le host surveille trois sources de logs, chacune avec ses propres règles de détection.
Pipeline complet
[Fichier log JSON]
│
[Agent Wazuh — logcollector]
│ (localfile, format json)
[Manager — analysisd]
│
[Décodeur json générique] ← toujours prioritaire sur les décodeurs custom
│
[Règles custom — field matching]
│
[alerts.json + Dashboard]
Un point clé : le décodeur json natif de Wazuh prend toujours la priorité sur les décodeurs personnalisés pour les logs JSON. Les règles utilisent donc <decoded_as>json</decoded_as> puis filtrent sur les champs pour identifier la source.
Règles Cowrie
<rule id="100200" level="6">
<decoded_as>json</decoded_as>
<field name="eventid">^cowrie\.</field>
<description>Cowrie honeypot event</description>
</rule>
<rule id="100201" level="10">
<if_sid>100200</if_sid>
<field name="eventid">^cowrie\.login\.failed$</field>
<description>Cowrie: SSH brute-force attempt</description>
<group>authentication_failed,</group>
</rule>
<rule id="100202" level="14">
<if_sid>100200</if_sid>
<field name="eventid">^cowrie\.login\.success$</field>
<description>Cowrie: Attacker authenticated to SSH honeypot</description>
<group>authentication_success,</group>
</rule>
Règles HTTP Honeypot
<rule id="100100" level="5">
<decoded_as>json</decoded_as>
<location>honeypot-access</location>
<description>HTTP Honeypot: requête reçue sur surface d'attaque</description>
</rule>
<rule id="100102" level="12">
<if_sid>100100</if_sid>
<field name="http_method">^POST$</field>
<description>HTTP Honeypot: credentials soumis au leurre</description>
<group>authentication_failed,</group>
</rule>
Règles Traefik
<rule id="100110" level="3">
<decoded_as>json</decoded_as>
<field name="RouterName">.</field>
<description>Traefik: requête HTTP reçue</description>
</rule>
<rule id="100111" level="6">
<if_sid>100110</if_sid>
<field name="DownstreamStatus">^404$</field>
<description>Traefik: ressource inexistante (404) — scan potentiel</description>
<group>recon,</group>
</rule>
Résumé des niveaux d’alerte
| ID | Level | Source | Déclencheur |
|---|---|---|---|
| 100100 | 5 | HTTP Honeypot | Toute requête sur le leurre |
| 100101 | 6 | HTTP Honeypot | GET sur interface sensible |
| 100102 | 12 | HTTP Honeypot | POST avec credentials |
| 100110 | 3 | Traefik | Requête HTTP reçue |
| 100111 | 6 | Traefik | 404 — scan potentiel |
| 100113 | 7 | Traefik | Erreur 5xx serveur |
| 100200 | 6 | Cowrie | Événement SSH générique |
| 100201 | 10 | Cowrie | Tentative de connexion SSH |
| 100202 | 14 | Cowrie | Connexion réussie au leurre |
| 100203 | 12 | Cowrie | Commande exécutée dans le leurre |
Le level 14 sur cowrie.login.success est intentionnel : si un attaquant passe l’authentification du honeypot SSH, c’est la situation la plus intéressante à analyser.
Déploiement Ansible
Tout est idempotent et se déploie via une seule commande. Les étapes clés liées à ce projet dans le playbook :
# Création des volumes partagés (honeypots → agent Wazuh)
- community.docker.docker_volume:
name: "{{ item }}"
loop: [honeypot_logs, cowrie_logs, traefik_logs]
# Surveillance des logs dans l'agent
- ansible.builtin.blockinfile:
path: /var/ossec/etc/ossec.conf
block: |
<localfile>
<log_format>json</log_format>
<location>/var/lib/docker/volumes/honeypot_logs/_data/honeypot-access.log</location>
</localfile>
# Attente que le manager soit opérationnel avant de déployer les règles
- ansible.builtin.shell: |
docker exec single-node-wazuh.manager-1 /var/ossec/bin/wazuh-control status \
| grep -c "is running"
until: wazuh_procs.stdout | int >= 5
retries: 24
delay: 15
# Déploiement des règles dans le volume Wazuh
- ansible.builtin.copy:
src: ../wazuh/cowrie_rules.xml
dest: /var/lib/docker/volumes/single-node_wazuh_etc/_data/rules/cowrie_rules.xml
Résultats
Le pipeline fonctionne de bout en bout. Voici une alerte Traefik réelle — accès au portfolio (règle 100110) suivi d’un 404 sur un path inexistant (règle 100111) :

Côté HTTP, les credentials WordPress soumis via POST apparaissent en clair dans data.credentials sur le Discover Wazuh. Voici un exemple de logs reçu :

Le dashboard Traefik permet quant à lui d’identifier les scanners automatiques : des dizaines de 404 en rafale sur des paths aléatoires, souvent depuis les mêmes ASN.
Analyse du Trafic
Analyse en cours — le serveur vient d’être déployé en production. Cette section sera mise à jour avec les données réelles : top IPs attaquantes, credentials les plus tentés, répartition géographique des scans SSH, commandes exécutées dans Cowrie, heatmap horaire des tentatives d’intrusion.
