TP 17 : Réponse Automatisée aux Incidents (Auto-Remédiation)
Module : 5 – NetSecDevOps et Sécurité Automatisée
Durée indicative : 2h
Prérequis : le OPNsense du TP 7 accessible, Python 3, pip install fastapi uvicorn requests pydantic python-dotenv
Objectifs
- Construire un mini-outil SOAR : réception d’alertes, décision, action.
- Automatiser le blocage d’une adresse IP compromise via l’API REST d’OPNsense.
- Mettre en œuvre les garde-fous vus en théorie (portée et durée limitées, journalisation).
Contexte
Un IDS/IPS enverrait normalement une alerte via un webhook lors de la détection d’un comportement suspect. Vous simulez cette alerte pour déclencher automatiquement un blocage temporaire sur le pare-feu.
De l’alerte simulée au blocage effectif sur OPNsense, avec garde-fou de déblocage automatique après une durée limitée.
Étape 1 – Serveur d’écoute des alertes (webhook)
soar_listener.py :
from fastapi import FastAPI
from pydantic import BaseModel
import logging
import uvicorn
app = FastAPI(title="SOAR Listener")
logging.basicConfig(filename="soar_actions.log", level=logging.INFO)
class Alert(BaseModel):
source_ip: str
severity: str = "low"
type: str | None = None
@app.post("/webhook/alert")
def receive_alert(alert: Alert):
logging.info(f"Alerte reçue : {alert.model_dump()}")
if alert.severity in ("high", "critical"):
block_ip(alert.source_ip)
return {"status": "blocked", "ip": alert.source_ip}
return {"status": "ignored"}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=5000)FastAPI valide automatiquement le corps JSON reçu grâce au modèle Pydantic Alert (types vérifiés, severity par défaut à "low" si absente), et expose une documentation interactive sur http://localhost:5000/docs.
Étape 2 – Fonction de blocage via l’API OPNsense
import os, requests
from dotenv import load_dotenv
load_dotenv()
BASE = f"https://{os.getenv('OPNSENSE_HOST')}/api"
AUTH = (os.getenv('OPNSENSE_KEY'), os.getenv('OPNSENSE_SECRET'))
def block_ip(ip):
payload = {"alias": {"name": "soar_blocklist", "address": ip}}
resp = requests.post(f"{BASE}/firewall/alias/addItem", json=payload, auth=AUTH, verify=False)
resp.raise_for_status()
requests.post(f"{BASE}/firewall/alias/reconfigure", auth=AUTH, verify=False)
logging.info(f"IP bloquée : {ip}")Prérequis OPNsense : créez au préalable un alias vide soar_blocklist (Firewall → Aliases) et une règle de blocage référençant cet alias, en amont des autres règles.
Étape 3 – Simulation d’une alerte
curl -X POST http://localhost:5000/webhook/alert \
-H "Content-Type: application/json" \
-d '{"source_ip": "203.0.113.50", "severity": "high", "type": "bruteforce_ssh"}'Vérifiez dans l’interface OPNsense que l’IP a été ajoutée à l’alias et que le trafic est bloqué.
Étape 4 – Garde-fous : durée limitée et liste blanche
Complétez block_ip pour :
- refuser de bloquer une IP présente dans une liste blanche codée en dur (ex. IP du poste hôte, de l’administrateur),
- programmer un déblocage automatique après une durée définie (ex. 1h), via un job planifié ou un thread différé (
threading.Timerà des fins pédagogiques).
Étape 5 – Journalisation et audit
Vérifiez que soar_actions.log trace bien : l’alerte reçue, la décision prise (bloqué/ignoré), l’IP concernée, l’horodatage.
Rendu attendu
soar_listener.pyet ses dépendances, versionnés (.envexclu).- Le fichier
soar_actions.logillustrant plusieurs scénarios (alerte bloquée, alerte ignorée, IP en liste blanche). - Une capture de l’alias
soar_blocklistpeuplé sur OPNsense.
Points de vérification
- Une alerte
severity: highdéclenche bien un blocage effectif sur OPNsense. - Une alerte
severity: lowest journalisée mais n’entraîne aucune action. - Une IP en liste blanche n’est jamais bloquée, même en cas d’alerte critique.