# [COURS] EXERCICES CORRIGÉS DHCP - ULTRA DÉTAILLÉS

## [LIVRE] INTRODUCTION

Ce document contient **5 exercices pratiques corrigés** sur DHCP (Dynamic Host Configuration Protocol), allant du niveau débutant au niveau expert. Chaque exercice suit la même méthodologie pédagogique que les exercices Apache.

**Niveau de progression :**
- [VERT] Exercice 1 : Débutant - Installation et configuration DHCP de base
- [JAUNE] Exercice 2 : Intermédiaire - Réservations et options avancées
- [JAUNE] Exercice 3 : Intermédiaire - Intégration DHCP + DNS dynamique (DDNS)
- [ROUGE] Exercice 4 : Avancé - Haute disponibilité DHCP (Failover)
- [ROUGE] Exercice 5 : Expert - Sécurisation et monitoring avancé

**Chaque exercice contient :**
- [OK] Énoncé détaillé avec contexte professionnel
- [OK] Prérequis et objectifs pédagogiques
- [OK] Solution complète étape par étape
- [OK] Code commenté ligne par ligne
- [OK] Tests de validation
- [OK] Erreurs courantes et solutions
- [OK] Points clés à retenir
- [OK] Pour aller plus loin

---

# [VERT] EXERCICE 1 : INSTALLATION ET CONFIGURATION DHCP DE BASE

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur réseau junior dans une PME de 50 employés. L'entreprise vient d'emménager dans de nouveaux bureaux et tu dois mettre en place un serveur DHCP pour automatiser l'attribution des adresses IP aux postes de travail, imprimantes et appareils mobiles.

### Cahier des charges

Le responsable IT te demande :
- **Réseau** : 192.168.10.0/24
- **Plage DHCP** : 192.168.10.100 à 192.168.10.200
- **Passerelle** : 192.168.10.1
- **Serveurs DNS** : 8.8.8.8 et 8.8.4.4
- **Durée de bail** : 24 heures (renouvellement à 12h)
- **Nom de domaine** : entreprise.local

### Contraintes techniques

- Serveur : Ubuntu 22.04 LTS
- Serveur DHCP : ISC DHCP Server
- Interface réseau : ens33 (à adapter selon ton système)
- Temps estimé : 1-2 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Comprendre le fonctionnement du protocole DHCP
- [OK] Installer ISC DHCP Server sur Linux
- [OK] Configurer un pool d'adresses IP
- [OK] Définir les options DHCP essentielles
- [OK] Démarrer et gérer le service DHCP
- [OK] Tester l'attribution d'adresses IP
- [OK] Consulter les baux DHCP actifs
- [OK] Déboguer les problèmes DHCP courants

---

## [DOCS] PRÉREQUIS

- Serveur Ubuntu 22.04 installé et à jour
- Droits root/sudo
- Interface réseau configurée en IP statique
- Compréhension basique du réseau TCP/IP
- Connaissances de base Linux (nano, systemctl)

---

## [IDEE] COMPRENDRE DHCP AVANT DE COMMENCER

### Qu'est-ce que DHCP ?

**DHCP** = Dynamic Host Configuration Protocol

**Rôle** : Attribuer automatiquement des paramètres réseau aux clients

**Paramètres distribués :**
- Adresse IP
- Masque de sous-réseau
- Passerelle par défaut
- Serveurs DNS
- Nom de domaine
- Serveurs NTP (temps)
- Et bien d'autres...

---

### Comment fonctionne DHCP ? (DORA)

**Le processus DHCP en 4 étapes :**

```
Client                          Serveur DHCP
  |                                  |
  |  1. DISCOVER (broadcast)         |
  |  "Y a-t-il un serveur DHCP ?"    |
  |--------------------------------->|
  |                                  |
  |  2. OFFER                        |
  |  "Voici une IP : 192.168.10.105" |
  |<---------------------------------|
  |                                  |
  |  3. REQUEST                      |
  |  "J'accepte cette IP"            |
  |--------------------------------->|
  |                                  |
  |  4. ACK                          |
  |  "C'est confirmé, elle est à toi"|
  |<---------------------------------|
  |                                  |
```

**Détails de chaque étape :**

**1. DISCOVER (Découverte)**
- Le client envoie un message en broadcast (255.255.255.255)
- "Y a-t-il un serveur DHCP sur ce réseau ?"
- Le client n'a pas encore d'adresse IP

**2. OFFER (Offre)**
- Le serveur DHCP répond avec une proposition
- "Voici une adresse IP disponible : 192.168.10.105"
- Propose aussi : masque, passerelle, DNS, durée de bail

**3. REQUEST (Requête)**
- Le client accepte l'offre
- "J'accepte l'IP 192.168.10.105"
- Envoie aussi en broadcast (au cas où plusieurs serveurs DHCP)

**4. ACK (Acknowledgment)**
- Le serveur confirme l'attribution
- "OK, 192.168.10.105 est à toi pour 24 heures"
- Le client configure son interface réseau

---

### Notion de bail (lease)

**Bail** = Durée pendant laquelle une IP est attribuée à un client

**Exemple :**
- Bail de 24 heures
- Le client peut utiliser l'IP pendant 24h
- Après 12h (50%), le client tente de renouveler (RENEW)
- Si échec, réessaie à 21h (87.5%) (REBIND)
- Après 24h sans renouvellement, l'IP est libérée

**Pourquoi des baux ?**
- Éviter le gaspillage d'adresses (machines éteintes)
- Permettre la réattribution d'IPs inutilisées
- Faciliter la reconfiguration du réseau

---

### Ports utilisés par DHCP

**Serveur DHCP** : Port UDP 67
**Client DHCP** : Port UDP 68

**[ATTENTION] Important pour les pare-feux !**

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Préparation du serveur

**Vérifier l'interface réseau :**

```bash
ip addr show
```

**Résultat (exemple) :**

```
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:12:34:56 brd ff:ff:ff:ff:ff:ff
    inet 192.168.10.10/24 brd 192.168.10.255 scope global ens33
       valid_lft forever preferred_lft forever
```

**Points importants :**
- **Interface** : `ens33` (à adapter : `eth0`, `enp0s3`, etc.)
- **IP du serveur** : `192.168.10.10` (doit être STATIQUE et en DEHORS de la plage DHCP)
- **Réseau** : `192.168.10.0/24`

**Si ton interface a un nom différent (ex: `eth0`), adapte toutes les commandes !**

---

**Vérifier que le serveur a une IP statique :**

```bash
cat /etc/netplan/*.yaml
```

**Exemple de configuration Netplan (Ubuntu 22.04) :**

```yaml
network:
  version: 2
  ethernets:
    ens33:
      addresses:
        - 192.168.10.10/24
      routes:
        - to: default
          via: 192.168.10.1
      nameservers:
        addresses: [8.8.8.8, 8.8.4.4]
```

**[ATTENTION] Le serveur DHCP DOIT avoir une IP statique !**

**Pourquoi ?**
- Le serveur ne peut pas s'attribuer sa propre IP via DHCP (paradoxe)
- Les clients doivent pouvoir le contacter à une adresse fixe

---

**Mettre à jour le système :**

```bash
sudo apt update
sudo apt upgrade -y
```

---

### ÉTAPE 2 : Installation d'ISC DHCP Server

```bash
sudo apt install isc-dhcp-server -y
```

**ISC DHCP Server** = Internet Systems Consortium DHCP Server
- Serveur DHCP le plus utilisé sur Linux/Unix
- Open-source, fiable, mature
- Alternative : dnsmasq (plus léger, pour petits réseaux)

---

**Vérifier l'installation :**

```bash
dpkg -l | grep isc-dhcp-server
```

**Résultat :**

```
ii  isc-dhcp-server  4.4.1-2.3ubuntu2  amd64  ISC DHCP server for automatic IP address assignment
```

**[OK] Installé !**

---

### ÉTAPE 3 : Configurer l'interface d'écoute

**Par défaut, le serveur DHCP ne sait pas sur quelle interface écouter.**

**Éditer le fichier de configuration des interfaces :**

```bash
sudo nano /etc/default/isc-dhcp-server
```

**Chercher la ligne `INTERFACESv4=""` et la modifier :**

```bash
# ═══════════════════════════════════════════════════════════════
# CONFIGURATION DES INTERFACES POUR ISC DHCP SERVER
# ═══════════════════════════════════════════════════════════════

# Interface(s) sur lesquelles le serveur DHCP écoute
# Plusieurs interfaces : INTERFACESv4="ens33 ens34"
INTERFACESv4="ens33"

# Pour IPv6 (optionnel)
INTERFACESv6=""
```

**Explication :**

**`INTERFACESv4="ens33"`**
- Le serveur DHCP écoute UNIQUEMENT sur l'interface `ens33`
- Il ignorera les requêtes DHCP sur d'autres interfaces

**Pourquoi spécifier l'interface ?**
- Sécurité : éviter de répondre sur des interfaces WAN (Internet)
- Performance : ne traiter que le trafic pertinent
- Isolation : segmenter les réseaux

**Plusieurs interfaces :**

```bash
INTERFACESv4="ens33 ens34"
```

Le serveur DHCP écoute sur `ens33` ET `ens34`.

**Sauvegarde : `Ctrl + O`, `Entrée`, `Ctrl + X`**

---

### ÉTAPE 4 : Configuration du serveur DHCP

**Éditer le fichier de configuration principal :**

```bash
sudo nano /etc/dhcp/dhcpd.conf
```

**Sauvegarder l'original (bonne pratique) :**

```bash
sudo cp /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd.conf.original
```

---

**Remplacer TOUT le contenu par :**

```conf
# ═══════════════════════════════════════════════════════════════
# CONFIGURATION ISC DHCP SERVER - EXERCICE 1
# ═══════════════════════════════════════════════════════════════
# Réseau : 192.168.10.0/24
# Plage DHCP : 192.168.10.100 - 192.168.10.200
# Date : Décembre 2024
# ═══════════════════════════════════════════════════════════════

# ───────────────────────────────────────────────────────────────
# PARAMÈTRES GLOBAUX
# ───────────────────────────────────────────────────────────────

# Ce serveur fait autorité sur ce réseau
authoritative;

# Explication :
# authoritative; = Ce serveur est le serveur DHCP PRINCIPAL
# 
# Avec "authoritative" :
# - Envoie des DHCPNAK si un client demande une mauvaise IP
# - Refuse les requêtes incohérentes
# - Comportement assertif
#
# Sans "authoritative" (mode non-autoritaire) :
# - Ignore les requêtes incohérentes
# - Comportement passif
# - Utile pour un serveur DHCP secondaire/backup
#
# [ATTENTION] En production, TOUJOURS mettre "authoritative;" !

# Durée du bail par défaut (en secondes)
default-lease-time 86400;

# Explication :
# 86400 secondes = 24 heures
# 
# Quand le client demande une IP sans spécifier de durée,
# cette valeur est utilisée
#
# Calcul :
# 24 heures × 60 minutes × 60 secondes = 86400 secondes
#
# Autres exemples :
# 1 heure = 3600
# 12 heures = 43200
# 7 jours = 604800

# Durée maximale du bail (en secondes)
max-lease-time 86400;

# Explication :
# Même si le client demande un bail plus long,
# le serveur ne donnera jamais plus de 86400 secondes
#
# Exemple :
# - Client demande 7 jours (604800 s)
# - Serveur donne seulement 86400 s (24h)
#
# Pourquoi limiter ?
# - Éviter qu'un client monopolise une IP trop longtemps
# - Permettre la reconfiguration du réseau
# - Libérer les IPs des machines éteintes

# Fichier de stockage des baux
ddns-update-style none;

# Explication :
# ddns-update-style = Dynamic DNS Update Style
# 
# none = Pas de mise à jour DNS dynamique (pour l'instant)
#
# Autres valeurs possibles :
# - interim = Style ancien (obsolète)
# - standard = Style moderne (voir exercice 3)
# - ad-hoc = Non standard (déconseillé)
#
# Pour l'instant, on désactive le DDNS (exercice 1 = basique)

# ───────────────────────────────────────────────────────────────
# DÉFINITION DU SOUS-RÉSEAU
# ───────────────────────────────────────────────────────────────

subnet 192.168.10.0 netmask 255.255.255.0 {
    # Déclaration du sous-réseau
    # subnet <adresse_réseau> netmask <masque_sous_réseau> { ... }
    #
    # 192.168.10.0 = Adresse du réseau
    # 255.255.255.0 = Masque de sous-réseau (/24)
    #
    # Ce bloc définit TOUS les paramètres pour ce réseau
    
    # ───────────────────────────────────────────────────────────
    # PLAGE D'ADRESSES IP À DISTRIBUER
    # ───────────────────────────────────────────────────────────
    
    range 192.168.10.100 192.168.10.200;
    
    # Explication :
    # Le serveur DHCP distribuera des adresses entre :
    # - 192.168.10.100 (première adresse)
    # - 192.168.10.200 (dernière adresse)
    #
    # Nombre d'adresses disponibles :
    # 200 - 100 + 1 = 101 adresses
    #
    # Adresses HORS de la plage :
    # - 192.168.10.1 à 192.168.10.99 : Réservées (serveurs, imprimantes, etc.)
    # - 192.168.10.201 à 192.168.10.254 : Réservées
    #
    # Bonnes pratiques :
    # - Ne jamais mettre 192.168.10.1 dans la plage (c'est souvent la passerelle)
    # - Réserver les premières IPs pour les équipements fixes
    # - Laisser une marge en fin de plage
    
    # ───────────────────────────────────────────────────────────
    # OPTIONS DHCP
    # ───────────────────────────────────────────────────────────
    
    # Option 1 : Masque de sous-réseau
    option subnet-mask 255.255.255.0;
    
    # Explication :
    # Indique aux clients quel est leur masque de sous-réseau
    # 
    # 255.255.255.0 = /24 en notation CIDR
    # Signifie : 
    # - Les 24 premiers bits identifient le réseau
    # - Les 8 derniers bits identifient l'hôte
    #
    # Réseau : 192.168.10.0
    # Plage utilisable : 192.168.10.1 à 192.168.10.254
    # Broadcast : 192.168.10.255
    #
    # Autres exemples :
    # 255.255.0.0 = /16 (65534 hôtes)
    # 255.255.255.128 = /25 (126 hôtes)
    
    # Option 3 : Passerelle par défaut
    option routers 192.168.10.1;
    
    # Explication :
    # Indique aux clients quelle est la passerelle (gateway/router)
    # 
    # 192.168.10.1 = Adresse de la passerelle
    # Généralement c'est le routeur qui donne accès à Internet
    #
    # Le client configurera sa table de routage :
    # Destination : 0.0.0.0/0 (toutes les adresses)
    # Passerelle : 192.168.10.1
    #
    # Plusieurs passerelles possibles :
    # option routers 192.168.10.1, 192.168.10.2;
    # Le client utilisera la première par défaut
    
    # Option 6 : Serveurs DNS
    option domain-name-servers 8.8.8.8, 8.8.4.4;
    
    # Explication :
    # Indique aux clients quels serveurs DNS utiliser
    #
    # 8.8.8.8 = Google DNS primaire
    # 8.8.4.4 = Google DNS secondaire
    #
    # Le client configurera /etc/resolv.conf (Linux) ou équivalent :
    # nameserver 8.8.8.8
    # nameserver 8.8.4.4
    #
    # Ordre d'utilisation :
    # 1. Essaie 8.8.8.8
    # 2. Si échec ou timeout, essaie 8.8.4.4
    #
    # Autres DNS populaires :
    # - 1.1.1.1, 1.0.0.1 (Cloudflare)
    # - 9.9.9.9 (Quad9)
    # - Serveur DNS interne : 192.168.10.10
    #
    # Jusqu'à 3 serveurs DNS :
    # option domain-name-servers 192.168.10.10, 8.8.8.8, 8.8.4.4;
    
    # Option 15 : Nom de domaine
    option domain-name "entreprise.local";
    
    # Explication :
    # Définit le suffixe DNS à ajouter aux noms d'hôtes
    #
    # Exemple :
    # - Nom d'hôte du client : "pc-comptable"
    # - Nom de domaine : "entreprise.local"
    # - FQDN complet : "pc-comptable.entreprise.local"
    #
    # Utilisé pour :
    # - Résolution DNS locale
    # - Identification des machines sur le réseau
    # - Création de noms d'hôtes qualifiés
    #
    # Le client configurera :
    # /etc/resolv.conf (Linux) :
    # domain entreprise.local
    # search entreprise.local
    #
    # Windows :
    # Suffixe DNS principal : entreprise.local
    
    # Option 51 : Durée du bail (en secondes)
    # Déjà défini globalement (default-lease-time)
    # Mais peut être redéfini ici pour ce subnet spécifiquement
    
    # ───────────────────────────────────────────────────────────
    # OPTIONS SUPPLÉMENTAIRES (OPTIONNELLES)
    # ───────────────────────────────────────────────────────────
    
    # Option 42 : Serveurs NTP (Network Time Protocol)
    # option ntp-servers 192.168.10.10;
    
    # Option 28 : Adresse de broadcast
    # option broadcast-address 192.168.10.255;
    # (Calculée automatiquement, rarement nécessaire)
    
    # Option 119 : Suffixes de recherche DNS additionnels
    # option domain-search "entreprise.local", "exemple.com";
}

# ═══════════════════════════════════════════════════════════════
# FIN DE LA CONFIGURATION
# ═══════════════════════════════════════════════════════════════

# NOTES IMPORTANTES :
#
# 1. Chaque déclaration se termine par un point-virgule ;
#
# 2. Les blocs { } définissent des portées
#
# 3. L'ordre des options n'a pas d'importance
#
# 4. Les commentaires # sont ignorés par le serveur
#
# 5. Toujours tester la configuration avant de redémarrer :
#    sudo dhcpd -t -cf /etc/dhcp/dhcpd.conf
```

**Sauvegarde : `Ctrl + O`, `Entrée`, `Ctrl + X`**

---

### ÉTAPE 5 : Vérifier la configuration

**Tester la syntaxe de la configuration :**

```bash
sudo dhcpd -t -cf /etc/dhcp/dhcpd.conf
```

**Options :**
- `-t` = Test mode (teste la config sans démarrer le serveur)
- `-cf` = Config File (spécifie le fichier de configuration)

**Résultat attendu :**

```
Internet Systems Consortium DHCP Server 4.4.1
Copyright 2004-2018 Internet Systems Consortium.
All rights reserved.
For info, please visit https://www.isc.org/software/dhcp/
Config file: /etc/dhcp/dhcpd.conf
Database file: /var/lib/dhcp/dhcpd.leases
PID file: /var/run/dhcpd.pid
```

**[OK] Pas d'erreur = Configuration valide !**

---

**Si erreur de syntaxe :**

```
/etc/dhcp/dhcpd.conf line 42: semicolon expected.
option domain-name "entreprise.local"
                                     ^
```

**Message clair :**
- Ligne 42
- Point-virgule manquant
- Indique l'emplacement exact de l'erreur

**Corriger et retester.**

---

### ÉTAPE 6 : Démarrer le serveur DHCP

```bash
sudo systemctl start isc-dhcp-server
```

---

**Vérifier l'état du service :**

```bash
sudo systemctl status isc-dhcp-server
```

**Résultat attendu :**

```
[BLACK_CIRCLE] isc-dhcp-server.service - ISC DHCP IPv4 server
     Loaded: loaded (/lib/systemd/system/isc-dhcp-server.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2024-12-16 18:00:00 UTC; 10s ago
       Docs: man:dhcpd(8)
   Main PID: 12345 (dhcpd)
      Tasks: 4 (limit: 4915)
     Memory: 5.2M
        CPU: 15ms
     CGroup: /system.slice/isc-dhcp-server.service
             └─12345 dhcpd -user dhcpd -group dhcpd -f -4 -pf /run/dhcp-server/dhcpd.pid -cf /etc/dhcp/dhcpd.conf ens33

Dec 16 18:00:00 dhcp-server dhcpd[12345]: Listening on LPF/ens33/00:0c:29:12:34:56/192.168.10.0/24
Dec 16 18:00:00 dhcp-server dhcpd[12345]: Sending on   LPF/ens33/00:0c:29:12:34:56/192.168.10.0/24
Dec 16 18:00:00 dhcp-server dhcpd[12345]: Sending on   Socket/fallback/fallback-net
```

**Points importants :**

**`Active: active (running)`**
- [OK] Le service tourne

**`Listening on LPF/ens33`**
- [OK] Écoute sur l'interface ens33

**`Sending on LPF/ens33`**
- [OK] Peut envoyer des réponses sur ens33

---

**Si le service ne démarre pas :**

```bash
sudo journalctl -xeu isc-dhcp-server.service
```

**Affiche les logs détaillés avec explications.**

---

**Activer le démarrage automatique au boot :**

```bash
sudo systemctl enable isc-dhcp-server
```

**Résultat :**

```
Synchronizing state of isc-dhcp-server.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable isc-dhcp-server
```

**[OK] Le service DHCP démarrera automatiquement au prochain redémarrage !**

---

### ÉTAPE 7 : Tester l'attribution d'adresses IP

**Méthode 1 : Avec une machine cliente**

**Sur une machine Linux (client) :**

```bash
# Libérer l'adresse IP actuelle
sudo dhclient -r ens33

# Demander une nouvelle adresse IP via DHCP
sudo dhclient -v ens33
```

**Option `-v` = Verbose (affiche les détails du processus DORA)**

**Résultat attendu :**

```
Internet Systems Consortium DHCP Client 4.4.1
Copyright 2004-2018 Internet Systems Consortium.
All rights reserved.
For info, please visit https://www.isc.org/software/dhcp/

Listening on LPF/ens33/00:0c:29:ab:cd:ef
Sending on   LPF/ens33/00:0c:29:ab:cd:ef
Sending on   Socket/fallback
DHCPDISCOVER on ens33 to 255.255.255.255 port 67 interval 3
DHCPOFFER of 192.168.10.100 from 192.168.10.10
DHCPREQUEST for 192.168.10.100 on ens33 to 255.255.255.255 port 67
DHCPACK of 192.168.10.100 from 192.168.10.10
bound to 192.168.10.100 -- renewal in 43200 seconds.
```

**Décortiquons :**

**`DHCPDISCOVER on ens33 to 255.255.255.255`**
- Étape 1 : DISCOVER
- Envoyé en broadcast (255.255.255.255)

**`DHCPOFFER of 192.168.10.100 from 192.168.10.10`**
- Étape 2 : OFFER
- Le serveur (192.168.10.10) propose l'IP 192.168.10.100

**`DHCPREQUEST for 192.168.10.100`**
- Étape 3 : REQUEST
- Le client accepte l'offre

**`DHCPACK of 192.168.10.100 from 192.168.10.10`**
- Étape 4 : ACK
- Le serveur confirme l'attribution

**`bound to 192.168.10.100 -- renewal in 43200 seconds`**
- IP attribuée : 192.168.10.100
- Renouvellement dans 43200 secondes (12 heures = 50% du bail)

---

**Vérifier la configuration réseau du client :**

```bash
ip addr show ens33
```

**Résultat :**

```
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:ab:cd:ef brd ff:ff:ff:ff:ff:ff
    inet 192.168.10.100/24 brd 192.168.10.255 scope global dynamic ens33
       valid_lft 86395sec preferred_lft 86395sec
```

**`inet 192.168.10.100/24`**
- [OK] IP attribuée par DHCP

**`dynamic`**
- [OK] Configuration dynamique (via DHCP)

**`valid_lft 86395sec`**
- [OK] Durée de validité du bail (≈24h)

---

**Vérifier la passerelle :**

```bash
ip route show
```

**Résultat :**

```
default via 192.168.10.1 dev ens33 proto dhcp src 192.168.10.100 metric 100
192.168.10.0/24 dev ens33 proto kernel scope link src 192.168.10.100
```

**`default via 192.168.10.1`**
- [OK] Passerelle configurée

---

**Vérifier les serveurs DNS :**

```bash
cat /etc/resolv.conf
```

**Résultat :**

```
# Generated by NetworkManager
nameserver 8.8.8.8
nameserver 8.8.4.4
search entreprise.local
```

**`nameserver 8.8.8.8`**
- [OK] DNS primaire configuré

**`search entreprise.local`**
- [OK] Nom de domaine configuré

---

**Méthode 2 : Simuler un client avec nmap**

**Si tu n'as pas de machine cliente, tu peux simuler avec `nmap` :**

```bash
sudo apt install nmap -y

# Simuler une requête DHCP
sudo nmap --script broadcast-dhcp-discover
```

**Résultat :**

```
Starting Nmap 7.80
Pre-scan script results:
| broadcast-dhcp-discover:
|   Response 1 of 1:
|     IP Offered: 192.168.10.101
|     DHCP Message Type: DHCPOFFER
|     Server Identifier: 192.168.10.10
|     Subnet Mask: 255.255.255.0
|     Router: 192.168.10.1
|     Domain Name Server: 8.8.8.8, 8.8.4.4
|     Domain Name: entreprise.local
|_    Lease Time: 1d00h00m00s
```

**[OK] Le serveur DHCP répond correctement !**

---

### ÉTAPE 8 : Consulter les baux DHCP actifs

**Sur le serveur DHCP :**

```bash
cat /var/lib/dhcp/dhcpd.leases
```

**Contenu (exemple) :**

```
# The format of this file is documented in the dhcpd.leases(5) manual page.
# This lease file was written by isc-dhcp-4.4.1

# authoring-byte-order entry is generated, DO NOT DELETE
authoring-byte-order little-endian;

lease 192.168.10.100 {
  starts 1 2024/12/16 18:05:32;
  ends 2 2024/12/17 18:05:32;
  cltt 1 2024/12/16 18:05:32;
  binding state active;
  next binding state free;
  rewind binding state free;
  hardware ethernet 00:0c:29:ab:cd:ef;
  uid "\001\000\014)\253\315\357";
  client-hostname "client-pc";
}
```

**Explication ligne par ligne :**

**`lease 192.168.10.100 {`**
- Bail pour l'adresse IP 192.168.10.100

**`starts 1 2024/12/16 18:05:32;`**
- Date de début du bail
- Format : jour_semaine année/mois/jour heure:minute:seconde
- 1 = Lundi

**`ends 2 2024/12/17 18:05:32;`**
- Date de fin du bail
- 2 = Mardi
- Durée = 24 heures

**`binding state active;`**
- État du bail : actif
- Autres états possibles :
  - free = Libre (non attribué)
  - abandoned = Abandonné (conflit détecté)
  - backup = En backup (failover)

**`hardware ethernet 00:0c:29:ab:cd:ef;`**
- Adresse MAC du client
- Identifie de manière unique la carte réseau

**`client-hostname "client-pc";`**
- Nom d'hôte du client
- Fourni par le client lors de la requête DHCP

---

**Voir uniquement les baux actifs :**

```bash
grep "binding state active" /var/lib/dhcp/dhcpd.leases -B 5
```

**Option `-B 5` = Affiche 5 lignes AVANT chaque correspondance**

---

**Compter le nombre de baux actifs :**

```bash
grep -c "binding state active" /var/lib/dhcp/dhcpd.leases
```

**Résultat : `1` (un seul client connecté)**

---

### ÉTAPE 9 : Consulter les logs DHCP

**Les logs DHCP sont dans syslog :**

```bash
sudo tail -f /var/log/syslog | grep dhcpd
```

**Option `-f` = Follow (suit les logs en temps réel)**

**Lors d'une attribution d'IP, tu verras :**

```
Dec 16 18:05:32 dhcp-server dhcpd[12345]: DHCPDISCOVER from 00:0c:29:ab:cd:ef via ens33
Dec 16 18:05:32 dhcp-server dhcpd[12345]: DHCPOFFER on 192.168.10.100 to 00:0c:29:ab:cd:ef via ens33
Dec 16 18:05:32 dhcp-server dhcpd[12345]: DHCPREQUEST for 192.168.10.100 (192.168.10.10) from 00:0c:29:ab:cd:ef via ens33
Dec 16 18:05:32 dhcp-server dhcpd[12345]: DHCPACK on 192.168.10.100 to 00:0c:29:ab:cd:ef via ens33
```

**Chaque ligne correspond à une étape du processus DORA.**

---

**Voir les dernières attributions :**

```bash
sudo grep DHCPACK /var/log/syslog | tail -10
```

---

**Voir les dernières requêtes refusées (DHCPNAK) :**

```bash
sudo grep DHCPNAK /var/log/syslog
```

**Un DHCPNAK est envoyé quand :**
- Le client demande une IP en dehors de la plage
- Le client demande une IP déjà attribuée
- Le serveur est "authoritative" et détecte une incohérence

---

### [OK] TESTS DE VALIDATION

**1. Service DHCP actif**

```bash
sudo systemctl is-active isc-dhcp-server
```

**Résultat attendu : `active`**

---

**2. Service écoute sur le bon port**

```bash
sudo netstat -ulnp | grep dhcpd
```

**Résultat attendu :**

```
udp   0   0 0.0.0.0:67   0.0.0.0:*   12345/dhcpd
```

**Port 67 (serveur DHCP) = [OK]**

---

**3. Attribution d'IP dans la bonne plage**

**Faire 5 requêtes DHCP (avec `nmap` ou clients réels) :**

```bash
for i in {1..5}; do sudo nmap --script broadcast-dhcp-discover | grep "IP Offered"; done
```

**Résultat attendu :**

```
|     IP Offered: 192.168.10.100
|     IP Offered: 192.168.10.101
|     IP Offered: 192.168.10.102
|     IP Offered: 192.168.10.103
|     IP Offered: 192.168.10.104
```

**[OK] Toutes les IPs sont dans la plage 192.168.10.100-200 !**

---

**4. Options DHCP correctes**

**Vérifier qu'un client reçoit bien :**
- [ ] Masque de sous-réseau : 255.255.255.0
- [ ] Passerelle : 192.168.10.1
- [ ] DNS : 8.8.8.8 et 8.8.4.4
- [ ] Nom de domaine : entreprise.local
- [ ] Durée de bail : 86400 secondes (24h)

**Utiliser `nmap --script broadcast-dhcp-discover` et vérifier chaque paramètre.**

---

**5. Renouvellement de bail**

**Simuler un renouvellement :**

**Sur le client :**

```bash
# Attendre 12 heures (ou modifier default-lease-time à 60 pour tester)
# Ou forcer un renouvellement :
sudo dhclient -r ens33
sudo dhclient ens33
```

**Sur le serveur, vérifier les logs :**

```bash
sudo grep DHCPREQUEST /var/log/syslog | tail -1
```

**Devrait montrer une requête de renouvellement.**

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "No subnet declaration for ens33"

**Message complet :**

```
No subnet declaration for ens33 (192.168.10.10).
** Ignoring requests on ens33. If this is not what you want, please write a subnet declaration in your dhcpd.conf file for the network segment to which interface ens33 is attached. **
```

**Cause :**

L'IP du serveur (192.168.10.10) n'est pas dans un subnet déclaré dans `dhcpd.conf`.

**Solution :**

**Vérifier l'IP du serveur :**

```bash
ip addr show ens33
```

**Vérifier le subnet dans `dhcpd.conf` :**

```conf
subnet 192.168.10.0 netmask 255.255.255.0 {
    ...
}
```

**L'IP du serveur (192.168.10.10) DOIT être dans le réseau 192.168.10.0/24.**

**Si l'IP du serveur est 192.168.1.10, il faut soit :**
- Changer le subnet dans `dhcpd.conf` pour `192.168.1.0`
- Ou changer l'IP du serveur pour qu'elle soit dans `192.168.10.0/24`

---

#### Erreur 2 : "Can't open /var/lib/dhcp/dhcpd.leases for append"

**Message complet :**

```
Can't open /var/lib/dhcp/dhcpd.leases for append: Permission denied
```

**Cause :**

Permissions incorrectes sur le fichier de baux.

**Solution :**

```bash
# Créer le fichier s'il n'existe pas
sudo touch /var/lib/dhcp/dhcpd.leases

# Définir les bonnes permissions
sudo chown dhcpd:dhcpd /var/lib/dhcp/dhcpd.leases
sudo chmod 644 /var/lib/dhcp/dhcpd.leases

# Redémarrer le service
sudo systemctl restart isc-dhcp-server
```

---

#### Erreur 3 : Le client ne reçoit pas d'IP

**Symptôme :**

Le client reste en `169.254.x.x` (APIPA - Automatic Private IP Addressing)

**Causes possibles :**

**1. Le serveur DHCP ne tourne pas**

```bash
sudo systemctl status isc-dhcp-server
```

**Si inactif, démarrer :**

```bash
sudo systemctl start isc-dhcp-server
```

---

**2. Le pare-feu bloque le port UDP 67**

```bash
sudo ufw status
```

**Si actif, autoriser DHCP :**

```bash
sudo ufw allow 67/udp
```

---

**3. Le client et le serveur ne sont pas sur le même réseau**

**Vérifier que le client et le serveur peuvent se pinguer.**

**Sur le serveur :**

```bash
ping -c 3 <IP_CLIENT>
```

**Si pas de réponse, problème réseau (switch, VLAN, etc.).**

---

**4. La plage DHCP est épuisée**

**Vérifier le nombre de baux actifs :**

```bash
grep -c "binding state active" /var/lib/dhcp/dhcpd.leases
```

**Si égal au nombre d'IPs dans la plage (101 dans notre exemple), agrandir la plage :**

```conf
range 192.168.10.50 192.168.10.250;
```

---

#### Erreur 4 : "Internet Systems Consortium DHCP Server 4.4.1 exited with return code 1"

**Symptôme :**

Le service DHCP ne démarre pas et retourne le code 1.

**Cause :**

Erreur de configuration dans `dhcpd.conf`.

**Solution :**

**Tester la configuration :**

```bash
sudo dhcpd -t -cf /etc/dhcp/dhcpd.conf
```

**Le message d'erreur indiquera la ligne et le problème.**

**Erreurs fréquentes :**
- Point-virgule manquant
- Accolade non fermée
- Option mal formatée
- Valeur incorrecte (ex: adresse IP invalide)

**Exemple :**

```
/etc/dhcp/dhcpd.conf line 35: semicolon expected.
option domain-name "entreprise.local"
                                     ^
```

**Ajouter le `;` en fin de ligne.**

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Processus DHCP (DORA)**
- **D**iscover : Le client cherche un serveur DHCP
- **O**ffer : Le serveur propose une IP
- **R**equest : Le client accepte
- **A**ck : Le serveur confirme

**2. Fichiers importants**
- `/etc/dhcp/dhcpd.conf` : Configuration principale
- `/etc/default/isc-dhcp-server` : Interfaces d'écoute
- `/var/lib/dhcp/dhcpd.leases` : Baux actifs
- `/var/log/syslog` : Logs du serveur DHCP

**3. Options DHCP essentielles**
- **Option 1** : Masque de sous-réseau
- **Option 3** : Passerelle
- **Option 6** : Serveurs DNS
- **Option 15** : Nom de domaine

**4. Bonnes pratiques**
- Toujours mettre le serveur en `authoritative`
- L'IP du serveur DHCP doit être statique
- L'IP du serveur doit être HORS de la plage DHCP
- Toujours tester la config avant de redémarrer : `dhcpd -t`
- Sauvegarder l'original : `dhcpd.conf.original`

**5. Ports réseau**
- Serveur DHCP : UDP 67
- Client DHCP : UDP 68

**6. Durée de bail**
- Default : Durée par défaut
- Max : Durée maximale
- Renouvellement : À 50% de la durée (T1)
- Rebinding : À 87.5% de la durée (T2)

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Plusieurs plages sur le même subnet**

```conf
subnet 192.168.10.0 netmask 255.255.255.0 {
    # Plage 1 : Postes de travail
    pool {
        range 192.168.10.100 192.168.10.150;
    }
    
    # Plage 2 : Appareils mobiles
    pool {
        range 192.168.10.151 192.168.10.200;
    }
    
    option routers 192.168.10.1;
    option domain-name-servers 8.8.8.8;
}
```

---

**2. Classes DHCP (différenciation par type de client)**

```conf
# Définir une classe pour les imprimantes
class "imprimantes" {
    match if substring (hardware, 1, 3) = 00:11:22;
}

subnet 192.168.10.0 netmask 255.255.255.0 {
    # Pool pour les imprimantes
    pool {
        allow members of "imprimantes";
        range 192.168.10.20 192.168.10.30;
    }
    
    # Pool pour les autres
    pool {
        deny members of "imprimantes";
        range 192.168.10.100 192.168.10.200;
    }
}
```

---

**3. Logs détaillés dans un fichier séparé**

**Créer le fichier de log :**

```bash
sudo touch /var/log/dhcpd.log
sudo chown syslog:adm /var/log/dhcpd.log
```

**Configurer rsyslog :**

```bash
sudo nano /etc/rsyslog.d/60-dhcpd.conf
```

**Contenu :**

```
if $programname == 'dhcpd' then /var/log/dhcpd.log
& stop
```

**Redémarrer rsyslog :**

```bash
sudo systemctl restart rsyslog
```

**Les logs DHCP seront maintenant dans `/var/log/dhcpd.log`.**

---

**4. Rotation des logs**

```bash
sudo nano /etc/logrotate.d/dhcpd
```

**Contenu :**

```
/var/log/dhcpd.log {
    weekly
    rotate 4
    compress
    delaycompress
    missingok
    notifempty
    create 640 syslog adm
    postrotate
        systemctl reload rsyslog > /dev/null 2>&1 || true
    endscript
}
```

---

**5. Interface web de monitoring : Webmin**

```bash
# Installer Webmin
wget http://www.webmin.com/download/deb/webmin-current.deb
sudo dpkg -i webmin-current.deb
sudo apt install -f
```

**Accéder à Webmin :**

```
https://<IP_SERVEUR>:10000
```

**Module DHCP Server permet de gérer la config via interface web.**

---

## [COURS] CONCLUSION DE L'EXERCICE 1

**[OK] Félicitations ! Tu as installé et configuré ton premier serveur DHCP !**

**Ce que tu as appris :**
- Comprendre le protocole DHCP (processus DORA)
- Installer ISC DHCP Server sur Linux
- Configurer un pool d'adresses IP
- Définir les options DHCP essentielles
- Gérer le service DHCP (démarrer, arrêter, vérifier)
- Consulter les baux actifs
- Analyser les logs DHCP
- Déboguer les problèmes courants

**Compétences acquises :**
- [OK] Installation DHCP (niveau débutant-intermédiaire)
- [OK] Configuration de base
- [OK] Gestion des baux
- [OK] Dépannage DHCP

**Temps moyen de réalisation :** 1-2 heures

**Prochaine étape :** Exercice 2 - Réservations DHCP et options avancées ! [OBJECTIF]

---

Je continue avec l'exercice 2 ?

# [JAUNE] EXERCICE 2 : RÉSERVATIONS DHCP ET OPTIONS AVANCÉES

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu continues ton travail d'administrateur réseau dans la PME. Après le déploiement réussi du serveur DHCP (exercice 1), le responsable IT te demande d'affiner la configuration pour répondre à de nouveaux besoins :

1. **Serveurs et imprimantes** : Doivent avoir des IP fixes mais gérées par DHCP
2. **Départements séparés** : Les équipes Marketing, Développement et Comptabilité doivent avoir des plages différentes
3. **Options spécifiques** : Les développeurs ont besoin d'options personnalisées (serveur proxy, boot PXE)
4. **VOIP** : Les téléphones IP nécessitent des paramètres spéciaux

### Cahier des charges

**Architecture réseau :**
```
Réseau : 192.168.10.0/24

Réservations (IP fixes) :
├─ Serveur fichiers : 192.168.10.5 (MAC: 00:11:22:33:44:55)
├─ Imprimante RH : 192.168.10.6 (MAC: 00:AA:BB:CC:DD:EE)
└─ Serveur web : 192.168.10.7 (MAC: 00:FF:EE:DD:CC:BB)

Plages dynamiques :
├─ Marketing : 192.168.10.100-120 (21 IPs)
├─ Développement : 192.168.10.121-150 (30 IPs)
├─ Comptabilité : 192.168.10.151-170 (20 IPs)
└─ Téléphones VOIP : 192.168.10.171-190 (20 IPs)

Options spéciales :
├─ Développeurs : Serveur proxy HTTP (192.168.10.50:3128)
├─ Développeurs : Serveur TFTP pour PXE (192.168.10.5)
└─ Téléphones VOIP : Serveur SIP (192.168.10.60)
```

### Contraintes techniques

- Serveur DHCP existant (exercice 1)
- Les réservations doivent être basées sur les adresses MAC
- Chaque département doit avoir des paramètres DNS différents
- Documentation complète de chaque paramètre
- Temps estimé : 2-3 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Créer des réservations DHCP (host declarations)
- [OK] Configurer plusieurs pools dans un même subnet
- [OK] Utiliser les classes DHCP pour segmenter les clients
- [OK] Définir des options DHCP personnalisées
- [OK] Configurer des options spécifiques par groupe
- [OK] Gérer les options DHCP avancées (proxy, TFTP, VOIP)
- [OK] Identifier les clients par adresse MAC
- [OK] Créer une architecture DHCP multi-départements

---

## [DOCS] PRÉREQUIS

- Exercice 1 terminé avec succès
- Serveur DHCP fonctionnel
- Compréhension des adresses MAC
- Connaissance des options DHCP de base

---

## [IDEE] COMPRENDRE LES RÉSERVATIONS DHCP

### Qu'est-ce qu'une réservation DHCP ?

**Réservation** = Associer une adresse IP spécifique à une adresse MAC

**Analogie :**
- **Plage dynamique** = Places libres dans un parking (premier arrivé, premier servi)
- **Réservation** = Place de parking avec ton nom dessus (toujours la même)

### Réservation vs IP statique classique

| Critère | Réservation DHCP | IP statique manuelle |
|---------|------------------|---------------------|
| **Configuration client** | Automatique via DHCP | Manuelle sur chaque machine |
| **Gestion centralisée** | [OK] Oui (dans dhcpd.conf) | [X] Non (sur chaque machine) |
| **Options DHCP** | [OK] Reçues (DNS, passerelle...) | [X] À configurer manuellement |
| **Flexibilité** | [OK] Facile à changer | [ATTENTION] Nécessite accès à la machine |
| **Audit** | [OK] Tracé dans les logs DHCP | [X] Difficile à suivre |

**En entreprise : TOUJOURS préférer les réservations DHCP !**

### Comment fonctionne une réservation ?

```
1. Serveur d'impression démarre
   MAC: 00:AA:BB:CC:DD:EE
   
2. Envoie DHCPDISCOVER

3. Serveur DHCP consulte dhcpd.conf
   "Cette MAC correspond à une réservation !"
   
4. Serveur DHCP offre TOUJOURS la même IP
   DHCPOFFER de 192.168.10.6
   
5. Le serveur d'impression reçoit 192.168.10.6
   À chaque fois, même après redémarrage !
```

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Récupérer les adresses MAC des équipements

**Avant de créer des réservations, il faut connaître les adresses MAC.**

**Méthode 1 : Sur l'équipement lui-même**

**Linux :**

```bash
ip link show | grep "link/ether"
```

**Résultat (exemple) :**

```
link/ether 00:11:22:33:44:55 brd ff:ff:ff:ff:ff:ff
```

**Windows :**

```cmd
ipconfig /all | findstr "Physique"
```

**Résultat :**

```
Adresse physique . . . . . . . . . . . : 00-11-22-33-44-55
```

---

**Méthode 2 : Depuis le serveur DHCP (si déjà connecté)**

```bash
cat /var/lib/dhcp/dhcpd.leases | grep "hardware ethernet"
```

**Résultat :**

```
hardware ethernet 00:11:22:33:44:55;
hardware ethernet 00:aa:bb:cc:dd:ee;
hardware ethernet 00:ff:ee:dd:cc:bb;
```

---

**Méthode 3 : Scanner le réseau avec nmap**

```bash
sudo nmap -sn 192.168.10.0/24
```

**Option `-sn` = Ping scan (pas de scan de ports)**

**Résultat :**

```
Nmap scan report for 192.168.10.5
Host is up (0.00012s latency).
MAC Address: 00:11:22:33:44:55 (VMware)

Nmap scan report for 192.168.10.25
Host is up (0.00023s latency).
MAC Address: 00:AA:BB:CC:DD:EE (HP)
```

---

**Méthode 4 : Consulter la table ARP**

```bash
ip neigh show
```

**Ou :**

```bash
arp -a
```

**Résultat :**

```
192.168.10.5 dev ens33 lladdr 00:11:22:33:44:55 REACHABLE
192.168.10.6 dev ens33 lladdr 00:aa:bb:cc:dd:ee STALE
```

---

**Note les adresses MAC nécessaires :**

```
Serveur fichiers : 00:11:22:33:44:55
Imprimante RH : 00:AA:BB:CC:DD:EE
Serveur web : 00:FF:EE:DD:CC:BB
```

---

### ÉTAPE 2 : Sauvegarder la configuration actuelle

```bash
sudo cp /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd.conf.backup-exercice1
```

**Toujours sauvegarder avant de modifier !**

---

### ÉTAPE 3 : Créer les réservations DHCP

**Éditer la configuration :**

```bash
sudo nano /etc/dhcp/dhcpd.conf
```

**Ajouter AVANT la déclaration du subnet (en haut du fichier) :**

```conf
# ═══════════════════════════════════════════════════════════════
# RÉSERVATIONS DHCP (HOST DECLARATIONS)
# ═══════════════════════════════════════════════════════════════
# Les réservations associent une IP fixe à une adresse MAC
# Elles doivent être déclarées EN DEHORS du bloc subnet
# ═══════════════════════════════════════════════════════════════

# ───────────────────────────────────────────────────────────────
# SERVEURS
# ───────────────────────────────────────────────────────────────

# Serveur de fichiers
host serveur-fichiers {
    # Identifiant unique de la réservation
    # "host <nom>" définit une réservation
    # Le nom peut être n'importe quoi (alphanumeric + tiret)
    # Utilisé pour identifier la réservation dans les logs
    
    # Adresse MAC du serveur de fichiers
    hardware ethernet 00:11:22:33:44:55;
    
    # Explication :
    # hardware ethernet = Type de matériel réseau
    # 00:11:22:33:44:55 = Adresse MAC (6 octets en hexadécimal)
    #
    # Format MAC :
    # - 6 paires de chiffres hexadécimaux
    # - Séparés par ":" ou "-"
    # - Exemple : 00:11:22:33:44:55 ou 00-11-22-33-44-55
    #
    # [ATTENTION] L'adresse MAC doit être UNIQUE sur le réseau !
    # Sinon, conflit et dysfonctionnement
    
    # Adresse IP fixe assignée
    fixed-address 192.168.10.5;
    
    # Explication :
    # fixed-address = IP TOUJOURS attribuée à cette MAC
    # 192.168.10.5 = IP réservée
    #
    # Cette IP sera offerte UNIQUEMENT à cette MAC
    # Même si elle est dans la plage dynamique (range),
    # elle sera exclue pour les autres clients
    #
    # Bonnes pratiques :
    # - Utiliser des IPs en DEHORS de la plage dynamique
    # - Facilite la gestion et évite les confusions
    # - Dans notre cas : plage dynamique = 100-200
    #   Réservations = 5-7 (en dehors)
    
    # Nom d'hôte (optionnel)
    option host-name "srv-fichiers";
    
    # Explication :
    # Définit le hostname du client
    # Le client configurera son nom d'hôte avec cette valeur
    #
    # Linux : hostname = srv-fichiers
    # Windows : Nom d'ordinateur = srv-fichiers
    #
    # Utile pour :
    # - Identification dans les logs
    # - DNS dynamique (exercice 3)
    # - Monitoring réseau
}

# Serveur web
host serveur-web {
    hardware ethernet 00:FF:EE:DD:CC:BB;
    fixed-address 192.168.10.7;
    option host-name "srv-web";
}

# ───────────────────────────────────────────────────────────────
# IMPRIMANTES
# ───────────────────────────────────────────────────────────────

# Imprimante du service RH
host imprimante-rh {
    hardware ethernet 00:AA:BB:CC:DD:EE;
    fixed-address 192.168.10.6;
    option host-name "print-rh";
    
    # Options spécifiques aux imprimantes (optionnel)
    # Durée de bail plus longue pour équipements fixes
    default-lease-time 604800;  # 7 jours
    max-lease-time 604800;
    
    # Explication :
    # 604800 secondes = 7 jours
    # 
    # Les imprimantes restent allumées en permanence
    # Pas besoin de renouveler le bail fréquemment
    # Réduit le trafic DHCP sur le réseau
    #
    # Calcul :
    # 7 jours × 24 heures × 60 min × 60 sec = 604800 s
}

# ═══════════════════════════════════════════════════════════════
```

**Explications supplémentaires sur les réservations :**

**Placement dans le fichier :**
- Les réservations (`host`) doivent être AVANT ou APRÈS le bloc `subnet`
- Jamais DANS le bloc `subnet`
- Convention : les mettre AVANT pour plus de clarté

**Portée des options :**
```conf
host serveur-fichiers {
    # Options définies ici s'appliquent UNIQUEMENT
    # à ce host (serveur-fichiers)
    
    option domain-name-servers 192.168.10.5;
    # Ce serveur utilisera 192.168.10.5 comme DNS
    # Et non les DNS globaux du subnet
}
```

---

### ÉTAPE 4 : Configurer les pools par département

**Modifier le bloc `subnet` existant :**

```conf
# ═══════════════════════════════════════════════════════════════
# DÉFINITION DU SOUS-RÉSEAU
# ═══════════════════════════════════════════════════════════════

subnet 192.168.10.0 netmask 255.255.255.0 {
    
    # ───────────────────────────────────────────────────────────
    # OPTIONS COMMUNES À TOUS LES DÉPARTEMENTS
    # ───────────────────────────────────────────────────────────
    
    # Ces options s'appliquent à TOUS les clients de ce subnet
    # Sauf si redéfinies dans un pool ou une classe
    
    option subnet-mask 255.255.255.0;
    option routers 192.168.10.1;
    option domain-name "entreprise.local";
    
    # DNS par défaut (peut être écrasé par pool)
    option domain-name-servers 8.8.8.8, 8.8.4.4;
    
    # Durée de bail par défaut
    default-lease-time 86400;  # 24 heures
    max-lease-time 86400;
    
    # ───────────────────────────────────────────────────────────
    # POOL 1 : DÉPARTEMENT MARKETING
    # ───────────────────────────────────────────────────────────
    
    pool {
        # Plage d'adresses pour le marketing
        range 192.168.10.100 192.168.10.120;
        
        # Explication :
        # pool { ... } définit un groupe d'adresses
        # avec des options spécifiques
        #
        # range = Plage d'IPs disponibles dans ce pool
        # 192.168.10.100-120 = 21 adresses
        #
        # Avantages des pools :
        # - Segmentation logique du réseau
        # - Options différentes par département
        # - Meilleure organisation
        # - Statistiques par pool
        
        # Options spécifiques au marketing
        option domain-name-servers 192.168.10.10, 8.8.8.8;
        
        # Explication :
        # Le marketing utilisera :
        # 1. Serveur DNS interne : 192.168.10.10
        # 2. Google DNS en backup : 8.8.8.8
        #
        # Cette option ÉCRASE les DNS globaux du subnet
        # Seulement pour les clients de ce pool
        
        # Nom de domaine spécifique (optionnel)
        option domain-name "marketing.entreprise.local";
        
        # Serveur NTP (temps) pour horodatage précis
        option ntp-servers 192.168.10.10;
        
        # Explication :
        # NTP = Network Time Protocol
        # Synchronise l'horloge des machines
        #
        # Utile pour :
        # - Horodatage précis des documents
        # - Synchronisation d'équipe
        # - Logs cohérents
    }
    
    # ───────────────────────────────────────────────────────────
    # POOL 2 : DÉPARTEMENT DÉVELOPPEMENT
    # ───────────────────────────────────────────────────────────
    
    pool {
        range 192.168.10.121 192.168.10.150;
        
        # Options spécifiques aux développeurs
        option domain-name-servers 192.168.10.10, 1.1.1.1;
        option domain-name "dev.entreprise.local";
        
        # Serveur proxy HTTP
        option wpad-url code 252 = text;
        option wpad-url "http://192.168.10.50:3128/proxy.pac";
        
        # Explication COMPLEXE (option personnalisée) :
        #
        # WPAD = Web Proxy Auto-Discovery
        # Permet aux navigateurs de découvrir le proxy automatiquement
        #
        # option wpad-url code 252 = text;
        # - Définit une NOUVELLE option DHCP custom
        # - Code 252 = Numéro d'option DHCP (réservé pour custom)
        # - text = Type de données (texte)
        #
        # option wpad-url "http://...";
        # - Définit la valeur de cette option
        # - URL du fichier PAC (Proxy Auto-Config)
        #
        # Le navigateur :
        # 1. Reçoit cette option via DHCP
        # 2. Télécharge proxy.pac
        # 3. Configure le proxy automatiquement
        #
        # Format du fichier PAC (JavaScript) :
        # function FindProxyForURL(url, host) {
        #     return "PROXY 192.168.10.50:3128";
        # }
        
        # Serveur TFTP pour PXE (démarrage réseau)
        next-server 192.168.10.5;
        filename "pxelinux.0";
        
        # Explication :
        # PXE = Preboot eXecution Environment
        # Permet de démarrer une machine via le réseau
        #
        # next-server 192.168.10.5;
        # - Adresse IP du serveur TFTP
        # - TFTP = Trivial File Transfer Protocol
        # - Protocole simple pour transférer des fichiers
        #
        # filename "pxelinux.0";
        # - Nom du fichier de boot à télécharger via TFTP
        # - pxelinux.0 = Bootloader PXE Linux
        #
        # Processus PXE :
        # 1. Machine démarre, pas d'OS
        # 2. Reçoit IP via DHCP
        # 3. Reçoit next-server et filename
        # 4. Contacte le serveur TFTP (192.168.10.5)
        # 5. Télécharge pxelinux.0
        # 6. Exécute pxelinux.0
        # 7. Boot sur l'OS réseau
        #
        # Utilisations :
        # - Installation d'OS automatisée
        # - Live boot Linux
        # - Diskless workstations
        # - Clonage de machines
    }
    
    # ───────────────────────────────────────────────────────────
    # POOL 3 : DÉPARTEMENT COMPTABILITÉ
    # ───────────────────────────────────────────────────────────
    
    pool {
        range 192.168.10.151 192.168.10.170;
        
        # DNS avec serveur interne prioritaire
        option domain-name-servers 192.168.10.10, 192.168.10.11, 8.8.8.8;
        option domain-name "compta.entreprise.local";
        
        # Bail plus court (sécurité accrue)
        default-lease-time 43200;  # 12 heures
        max-lease-time 43200;
        
        # Explication :
        # La comptabilité manipule des données sensibles
        # Baux plus courts = meilleur contrôle
        #
        # 43200 secondes = 12 heures
        # Calcul : 12 × 60 × 60 = 43200
        #
        # Avantages :
        # - Détection plus rapide des machines éteintes
        # - Renouvellement plus fréquent (audit)
        # - Possibilité de changer la config plus vite
        #
        # Inconvénient :
        # - Plus de trafic DHCP sur le réseau
    }
    
    # ───────────────────────────────────────────────────────────
    # POOL 4 : TÉLÉPHONES VOIP
    # ───────────────────────────────────────────────────────────
    
    pool {
        range 192.168.10.171 192.168.10.190;
        
        # Options spécifiques VOIP
        option domain-name "voip.entreprise.local";
        
        # VLAN ID pour VOIP (option 132)
        option voip-vlan-id code 132 = unsigned integer 16;
        option voip-vlan-id 20;
        
        # Explication :
        # VLAN = Virtual LAN (réseau local virtuel)
        # Sépare logiquement le trafic VOIP du trafic data
        #
        # option voip-vlan-id code 132 = unsigned integer 16;
        # - Définit une option custom
        # - Code 132 (vendor-specific)
        # - unsigned integer 16 = Entier non signé sur 16 bits
        #
        # option voip-vlan-id 20;
        # - VLAN ID = 20
        # - Les téléphones tagueront leurs paquets avec VLAN 20
        #
        # Avantages :
        # - Qualité de Service (QoS) pour la VOIP
        # - Priorité du trafic voix sur le data
        # - Sécurité (isolation)
        
        # Serveur SIP (Signaling)
        option sip-server code 120 = text;
        option sip-server "192.168.10.60";
        
        # Explication :
        # SIP = Session Initiation Protocol
        # Protocole de signalisation pour VOIP
        #
        # option sip-server code 120 = text;
        # - Option standard DHCP pour VOIP
        # - Code 120 réservé pour SIP
        #
        # option sip-server "192.168.10.60";
        # - Adresse du serveur SIP (Asterisk, FreePBX, etc.)
        #
        # Le téléphone :
        # 1. Reçoit cette option via DHCP
        # 2. Se connecte au serveur SIP (192.168.10.60)
        # 3. S'enregistre (authentification)
        # 4. Prêt à passer/recevoir des appels
        
        # Serveur TFTP pour configuration des téléphones
        next-server 192.168.10.60;
        
        # Explication :
        # Beaucoup de téléphones IP téléchargent leur config via TFTP
        #
        # Fichiers téléchargés (exemples) :
        # - SEPxxxxxxxxxxxx.cnf.xml (Cisco)
        # - 001122334455.cfg (Polycom - basé sur MAC)
        # - snom300-firmware.bin (mise à jour firmware)
        #
        # Processus :
        # 1. Téléphone démarre
        # 2. Obtient IP via DHCP
        # 3. Reçoit next-server (192.168.10.60)
        # 4. Contacte le serveur TFTP
        # 5. Télécharge sa config (basée sur MAC ou modèle)
        # 6. Applique la config (codec, touches, etc.)
        
        # Bail long pour les téléphones (équipements fixes)
        default-lease-time 604800;  # 7 jours
        max-lease-time 2592000;     # 30 jours
        
        # Explication :
        # Les téléphones IP :
        # - Restent allumés 24/7
        # - Ne changent presque jamais de place
        # - Bénéficient de baux longs
        #
        # 2592000 secondes = 30 jours
        # Calcul : 30 × 24 × 60 × 60 = 2592000
    }
}

# ═══════════════════════════════════════════════════════════════
# FIN DE LA CONFIGURATION
# ═══════════════════════════════════════════════════════════════
```

**Sauvegarde : `Ctrl + O`, `Entrée`, `Ctrl + X`**

---

### ÉTAPE 5 : Tester la configuration

**Vérifier la syntaxe :**

```bash
sudo dhcpd -t -cf /etc/dhcp/dhcpd.conf
```

**Résultat attendu :**

```
Internet Systems Consortium DHCP Server 4.4.1
Copyright 2004-2018 Internet Systems Consortium.
All rights reserved.
For info, please visit https://www.isc.org/software/dhcp/
Config file: /etc/dhcp/dhcpd.conf
Database file: /var/lib/dhcp/dhcpd.leases
PID file: /var/run/dhcpd.pid
```

**[OK] Pas d'erreur = Configuration valide !**

---

**Redémarrer le service DHCP :**

```bash
sudo systemctl restart isc-dhcp-server
```

**Vérifier l'état :**

```bash
sudo systemctl status isc-dhcp-server
```

**Devrait afficher `Active: active (running)`.**

---

### ÉTAPE 6 : Tester les réservations

**Méthode 1 : Avec la vraie machine**

**Sur le serveur de fichiers (MAC: 00:11:22:33:44:55) :**

```bash
# Libérer l'IP actuelle
sudo dhclient -r ens33

# Demander une nouvelle IP
sudo dhclient -v ens33
```

**Résultat attendu :**

```
DHCPDISCOVER on ens33 to 255.255.255.255 port 67 interval 3
DHCPOFFER of 192.168.10.5 from 192.168.10.10
DHCPREQUEST for 192.168.10.5 on ens33 to 255.255.255.255 port 67
DHCPACK of 192.168.10.5 from 192.168.10.10
bound to 192.168.10.5 -- renewal in 302400 seconds.
```

**Points importants :**

**`DHCPOFFER of 192.168.10.5`**
- [OK] IP réservée (192.168.10.5) offerte !
- Pas une IP de la plage dynamique (100-200)

**`renewal in 302400 seconds`**
- 302400 s = 84 heures = 3,5 jours
- Si on avait défini un bail spécifique dans le host

---

**Vérifier l'IP obtenue :**

```bash
ip addr show ens33 | grep "inet "
```

**Résultat :**

```
inet 192.168.10.5/24 brd 192.168.10.255 scope global dynamic ens33
```

**[OK] IP = 192.168.10.5 (réservation fonctionnelle) !**

---

**Méthode 2 : Consulter les logs du serveur DHCP**

**Sur le serveur DHCP :**

```bash
sudo tail -20 /var/log/syslog | grep dhcpd
```

**Résultat :**

```
Dec 16 20:00:00 dhcp-server dhcpd[12345]: DHCPDISCOVER from 00:11:22:33:44:55 via ens33
Dec 16 20:00:00 dhcp-server dhcpd[12345]: DHCPOFFER on 192.168.10.5 to 00:11:22:33:44:55 (serveur-fichiers) via ens33
Dec 16 20:00:01 dhcp-server dhcpd[12345]: DHCPREQUEST for 192.168.10.5 (192.168.10.10) from 00:11:22:33:44:55 (serveur-fichiers) via ens33
Dec 16 20:00:01 dhcp-server dhcpd[12345]: DHCPACK on 192.168.10.5 to 00:11:22:33:44:55 (serveur-fichiers) via ens33
```

**Points importants :**

**`00:11:22:33:44:55 (serveur-fichiers)`**
- [OK] Le serveur a identifié la MAC
- [OK] Associée au nom "serveur-fichiers"

**`DHCPOFFER on 192.168.10.5`**
- [OK] IP réservée offerte

---

**Méthode 3 : Vérifier dans le fichier de baux**

```bash
cat /var/lib/dhcp/dhcpd.leases | grep -A 10 "192.168.10.5"
```

**Résultat :**

```
lease 192.168.10.5 {
  starts 1 2024/12/16 20:00:01;
  ends 4 2024/12/19 20:00:01;
  cltt 1 2024/12/16 20:00:01;
  binding state active;
  next binding state free;
  rewind binding state free;
  hardware ethernet 00:11:22:33:44:55;
  uid "\001\000\021\"3DU";
  client-hostname "serveur-fichiers";
}
```

**`hardware ethernet 00:11:22:33:44:55;`**
- [OK] Correspond à notre réservation

**`client-hostname "serveur-fichiers";`**
- [OK] Hostname défini dans la réservation

---

### ÉTAPE 7 : Tester les pools par département

**Simuler un client du département Marketing :**

**Sur une machine de test (ou VM), changer temporairement la MAC pour qu'elle ne corresponde à aucune réservation.**

**Ou utiliser `nmap` :**

```bash
sudo nmap --script broadcast-dhcp-discover -e ens33
```

**[ATTENTION] `nmap` génère une MAC aléatoire, donc ne correspondra pas aux réservations.**

**Résultat attendu (pool aléatoire) :**

```
|     IP Offered: 192.168.10.115
|     DHCP Message Type: DHCPOFFER
|     Server Identifier: 192.168.10.10
|     Subnet Mask: 255.255.255.0
|     Router: 192.168.10.1
|     Domain Name Server: 192.168.10.10, 8.8.8.8
|     Domain Name: marketing.entreprise.local
```

**Points importants :**

**`IP Offered: 192.168.10.115`**
- [OK] IP dans la plage 100-120 (Marketing)
- Le serveur a choisi un pool aléatoirement

**`Domain Name: marketing.entreprise.local`**
- [OK] Option spécifique au pool Marketing

---

**Pour tester un pool spécifique, il faudrait utiliser les classes DHCP (voir ÉTAPE 8).**

---

### ÉTAPE 8 : Utiliser les classes DHCP pour auto-assignation

**Problème actuel :**
- Les clients obtiennent une IP d'un pool aléatoire
- Pas de contrôle sur quel client va dans quel département

**Solution : Classes DHCP**
- Identifier les clients par un critère (MAC, hostname, etc.)
- Les assigner automatiquement au bon pool

---

**Exemple : Assigner automatiquement selon le préfixe MAC**

**Ajouter AVANT le bloc `subnet` :**

```conf
# ═══════════════════════════════════════════════════════════════
# CLASSES DHCP POUR AUTO-ASSIGNATION PAR DÉPARTEMENT
# ═══════════════════════════════════════════════════════════════

# Classe pour le département Marketing
class "marketing" {
    # Matcher les clients dont la MAC commence par 00:11:22
    match if substring (hardware, 1, 3) = 00:11:22;
    
    # Explication COMPLEXE :
    #
    # match if = Condition de correspondance
    #
    # substring (hardware, 1, 3)
    # - substring() = Fonction pour extraire une sous-chaîne
    # - hardware = Adresse matérielle (MAC)
    # - 1 = Position de départ (commence à 0)
    # - 3 = Nombre d'octets à extraire
    #
    # Décomposition de l'adresse MAC :
    # 00:11:22:33:44:55
    # v  v  v  v  v  v
    # 0  1  2  3  4  5  (positions)
    #
    # substring (hardware, 1, 3) extrait :
    # Position 1, 2, 3 = 11:22:33
    #
    # = 00:11:22
    # Compare avec 00:11:22 (OUI du fabricant)
    #
    # OUI = Organizationally Unique Identifier
    # Les 3 premiers octets identifient le fabricant
    #
    # Exemples d'OUI :
    # - 00:11:22 = (exemple fictif pour cet exercice)
    # - 00:50:56 = VMware
    # - 08:00:27 = VirtualBox
    # - 00:0C:29 = VMware aussi
    # - 00:1A:A0 = Dell
    # - 00:23:DF = Apple
    #
    # Utilisation :
    # Si on achète des PC Dell pour le marketing,
    # on peut les assigner automatiquement au pool Marketing
    # basé sur leur OUI Dell
}

# Classe pour le département Développement
class "developpement" {
    # Matcher les clients dont le hostname commence par "dev-"
    match if substring (option host-name, 0, 4) = "dev-";
    
    # Explication :
    #
    # option host-name = Hostname envoyé par le client
    # Lors de la requête DHCP, le client peut envoyer son hostname
    #
    # substring (option host-name, 0, 4)
    # - Position 0 = Début de la chaîne
    # - 4 caractères
    # - Extrait les 4 premiers caractères
    #
    # Exemple :
    # Hostname client = "dev-pc-alice"
    # substring (option host-name, 0, 4) = "dev-"
    # Correspondance ! -> Assigner au pool Développement
    #
    # Convention de nommage :
    # - Marketing : mrk-pc-*
    # - Développement : dev-pc-*
    # - Comptabilité : cpt-pc-*
    #
    # Avantage :
    # Auto-assignation basée sur une convention claire
}

# Classe pour le département Comptabilité
class "comptabilite" {
    # Matcher les clients dont la MAC commence par 00:AA:BB
    match if substring (hardware, 1, 3) = 00:AA:BB;
}

# Classe pour les téléphones VOIP
class "voip" {
    # Matcher les clients dont le vendor-class-identifier contient "Cisco"
    match if substring (option vendor-class-identifier, 0, 5) = "Cisco";
    
    # Explication :
    #
    # vendor-class-identifier = Identifiant du fabricant
    # Option DHCP envoyée par le client (option 60)
    #
    # Exemples de vendor-class-identifier :
    # - "Cisco Systems, Inc. IP Phone CP-7960"
    # - "Polycom"
    # - "Yealink"
    # - "Grandstream"
    #
    # substring (option vendor-class-identifier, 0, 5) = "Cisco"
    # - Extrait les 5 premiers caractères
    # - Compare avec "Cisco"
    #
    # Si correspondance -> Téléphone Cisco IP
    # -> Assigner au pool VOIP
    #
    # Autres méthodes de détection VOIP :
    # - OUI du fabricant (MAC)
    # - User-class (option 77)
    # - Requête d'options spécifiques (120 pour SIP)
}
```

---

**Modifier les pools pour utiliser les classes :**

```conf
subnet 192.168.10.0 netmask 255.255.255.0 {
    
    # Options globales
    option subnet-mask 255.255.255.0;
    option routers 192.168.10.1;
    option domain-name "entreprise.local";
    
    # ───────────────────────────────────────────────────────────
    # POOL 1 : MARKETING (avec classe)
    # ───────────────────────────────────────────────────────────
    
    pool {
        # Autoriser SEULEMENT les membres de la classe "marketing"
        allow members of "marketing";
        
        # Explication :
        # allow members of "marketing";
        # - Seuls les clients matchant la classe "marketing"
        #   peuvent obtenir une IP de ce pool
        #
        # deny members of "marketing";
        # - Les clients matchant "marketing" NE PEUVENT PAS
        #   obtenir une IP de ce pool
        #
        # Si ni allow ni deny :
        # - Tous les clients peuvent obtenir une IP (défaut)
        
        range 192.168.10.100 192.168.10.120;
        option domain-name-servers 192.168.10.10, 8.8.8.8;
        option domain-name "marketing.entreprise.local";
        option ntp-servers 192.168.10.10;
    }
    
    # ───────────────────────────────────────────────────────────
    # POOL 2 : DÉVELOPPEMENT (avec classe)
    # ───────────────────────────────────────────────────────────
    
    pool {
        allow members of "developpement";
        
        range 192.168.10.121 192.168.10.150;
        option domain-name-servers 192.168.10.10, 1.1.1.1;
        option domain-name "dev.entreprise.local";
        
        # Options PXE et proxy (identiques à avant)
        option wpad-url code 252 = text;
        option wpad-url "http://192.168.10.50:3128/proxy.pac";
        next-server 192.168.10.5;
        filename "pxelinux.0";
    }
    
    # ───────────────────────────────────────────────────────────
    # POOL 3 : COMPTABILITÉ (avec classe)
    # ───────────────────────────────────────────────────────────
    
    pool {
        allow members of "comptabilite";
        
        range 192.168.10.151 192.168.10.170;
        option domain-name-servers 192.168.10.10, 192.168.10.11, 8.8.8.8;
        option domain-name "compta.entreprise.local";
        default-lease-time 43200;
        max-lease-time 43200;
    }
    
    # ───────────────────────────────────────────────────────────
    # POOL 4 : VOIP (avec classe)
    # ───────────────────────────────────────────────────────────
    
    pool {
        allow members of "voip";
        
        range 192.168.10.171 192.168.10.190;
        option domain-name "voip.entreprise.local";
        
        # Options VOIP (identiques à avant)
        option voip-vlan-id code 132 = unsigned integer 16;
        option voip-vlan-id 20;
        option sip-server code 120 = text;
        option sip-server "192.168.10.60";
        next-server 192.168.10.60;
        
        default-lease-time 604800;
        max-lease-time 2592000;
    }
    
    # ───────────────────────────────────────────────────────────
    # POOL 5 : FALLBACK (clients non identifiés)
    # ───────────────────────────────────────────────────────────
    
    pool {
        # Refuser les membres des classes connues
        deny members of "marketing";
        deny members of "developpement";
        deny members of "comptabilite";
        deny members of "voip";
        
        # Explication :
        # Ce pool est pour les clients qui ne matchent AUCUNE classe
        # Invités, machines temporaires, etc.
        #
        # deny members of ... :
        # Si un client matche une de ces classes,
        # il NE PEUT PAS obtenir une IP de ce pool
        #
        # Résultat :
        # Seuls les clients "inconnus" obtiennent une IP ici
        
        range 192.168.10.201 192.168.10.220;
        
        # Options minimales
        option domain-name-servers 8.8.8.8, 8.8.4.4;
        
        # Bail court (invités temporaires)
        default-lease-time 3600;  # 1 heure
        max-lease-time 7200;      # 2 heures
        
        # Explication :
        # Les invités n'ont besoin d'une IP que temporairement
        # Baux courts permettent :
        # - Rotation rapide des IPs
        # - Sécurité (limitation dans le temps)
        # - Détection rapide des machines inactives
    }
}
```

---

**Tester la syntaxe :**

```bash
sudo dhcpd -t -cf /etc/dhcp/dhcpd.conf
```

**Redémarrer :**

```bash
sudo systemctl restart isc-dhcp-server
```

---

### ÉTAPE 9 : Tester les classes et l'auto-assignation

**Test 1 : Client avec MAC commençant par 00:11:22 (Marketing)**

**Si tu as une VM, change temporairement sa MAC :**

**VirtualBox :**
```
Configuration -> Réseau -> Avancé -> Adresse MAC
-> 001122AABBCC
```

**VMware :**
```
Edit Virtual Machine -> Network Adapter -> Advanced
-> MAC Address: 00:11:22:AA:BB:CC
```

**Demander une IP :**

```bash
sudo dhclient -v ens33
```

**Résultat attendu :**

```
DHCPOFFER of 192.168.10.105 from 192.168.10.10
...
bound to 192.168.10.105
```

**Vérifier le domaine :**

```bash
cat /etc/resolv.conf | grep search
```

**Résultat :**

```
search marketing.entreprise.local
```

**[OK] Client auto-assigné au pool Marketing !**

---

**Test 2 : Client avec hostname "dev-pc-test" (Développement)**

**Sur le client, définir le hostname :**

```bash
sudo hostnamectl set-hostname dev-pc-test
```

**Demander une IP :**

```bash
sudo dhclient -v ens33
```

**Résultat attendu :**

```
DHCPOFFER of 192.168.10.135 from 192.168.10.10
...
bound to 192.168.10.135
```

**Vérifier le domaine :**

```bash
cat /etc/resolv.conf | grep search
```

**Résultat :**

```
search dev.entreprise.local
```

**[OK] Client auto-assigné au pool Développement !**

---

**Test 3 : Client non identifié (Fallback)**

**MAC ne commençant ni par 00:11:22 ni 00:AA:BB**
**Hostname ne commençant pas par "dev-"**

**Exemple : MAC = 00:0C:29:12:34:56**

**Résultat attendu :**

```
DHCPOFFER of 192.168.10.205 from 192.168.10.10
```

**IP dans la plage 201-220 (pool Fallback) [OK]**

---

### ÉTAPE 10 : Documenter la configuration

**Créer un fichier de documentation :**

```bash
sudo nano /etc/dhcp/DOCUMENTATION.md
```

**Contenu :**

```markdown
# Documentation Serveur DHCP - Entreprise

## Résumé de la configuration

- **Réseau** : 192.168.10.0/24
- **Serveur DHCP** : 192.168.10.10
- **Passerelle** : 192.168.10.1

## Réservations (IP fixes)

| Équipement | IP | Adresse MAC | Hostname |
|------------|-----|-------------|----------|
| Serveur fichiers | 192.168.10.5 | 00:11:22:33:44:55 | srv-fichiers |
| Imprimante RH | 192.168.10.6 | 00:AA:BB:CC:DD:EE | print-rh |
| Serveur web | 192.168.10.7 | 00:FF:EE:DD:CC:BB | srv-web |

## Pools DHCP par département

### Marketing (192.168.10.100-120)

- **Plage** : 21 adresses
- **DNS** : 192.168.10.10, 8.8.8.8
- **Domaine** : marketing.entreprise.local
- **Critère d'assignation** : MAC commençant par 00:11:22

### Développement (192.168.10.121-150)

- **Plage** : 30 adresses
- **DNS** : 192.168.10.10, 1.1.1.1
- **Domaine** : dev.entreprise.local
- **Proxy HTTP** : 192.168.10.50:3128
- **Serveur TFTP/PXE** : 192.168.10.5
- **Critère d'assignation** : Hostname commençant par "dev-"

### Comptabilité (192.168.10.151-170)

- **Plage** : 20 adresses
- **DNS** : 192.168.10.10, 192.168.10.11, 8.8.8.8
- **Domaine** : compta.entreprise.local
- **Durée de bail** : 12 heures
- **Critère d'assignation** : MAC commençant par 00:AA:BB

### VOIP (192.168.10.171-190)

- **Plage** : 20 téléphones
- **VLAN ID** : 20
- **Serveur SIP** : 192.168.10.60
- **Serveur TFTP** : 192.168.10.60
- **Durée de bail** : 7-30 jours
- **Critère d'assignation** : Vendor-class contenant "Cisco"

### Fallback / Invités (192.168.10.201-220)

- **Plage** : 20 adresses
- **DNS** : 8.8.8.8, 8.8.4.4
- **Durée de bail** : 1-2 heures
- **Critère** : Clients non identifiés

## Procédures

### Ajouter une nouvelle réservation

1. Récupérer l'adresse MAC :
   ```bash
   ip link show
   ```

2. Éditer `/etc/dhcp/dhcpd.conf`

3. Ajouter avant le bloc `subnet` :
   ```conf
   host nouveau-serveur {
       hardware ethernet XX:XX:XX:XX:XX:XX;
       fixed-address 192.168.10.X;
       option host-name "nom-serveur";
   }
   ```

4. Tester et redémarrer :
   ```bash
   sudo dhcpd -t -cf /etc/dhcp/dhcpd.conf
   sudo systemctl restart isc-dhcp-server
   ```

### Consulter les baux actifs

```bash
cat /var/lib/dhcp/dhcpd.leases | grep "binding state active" -B 5
```

### Consulter les logs

```bash
sudo tail -f /var/log/syslog | grep dhcpd
```

## Contacts

- **Administrateur réseau** : admin@entreprise.local
- **Support IT** : +221 77 123 45 67
```

**Sauvegarde.**

---

### [OK] TESTS DE VALIDATION

**1. Toutes les réservations fonctionnent**

```bash
# Vérifier dans les logs que les MACs réservées obtiennent les bonnes IPs
sudo grep "00:11:22:33:44:55" /var/log/syslog | grep DHCPACK
```

**Résultat attendu :**

```
DHCPACK on 192.168.10.5 to 00:11:22:33:44:55 (serveur-fichiers)
```

- [ ] Serveur fichiers -> 192.168.10.5
- [ ] Imprimante RH -> 192.168.10.6
- [ ] Serveur web -> 192.168.10.7

---

**2. Les pools séparent correctement les départements**

```bash
# Voir la répartition des IPs attribuées
grep "binding state active" /var/lib/dhcp/dhcpd.leases -B 5 | grep "lease " | awk '{print $2}' | sort -V
```

**Résultat (exemple) :**

```
192.168.10.5
192.168.10.6
192.168.10.7
192.168.10.105
192.168.10.106
192.168.10.135
192.168.10.160
192.168.10.180
192.168.10.205
```

**Vérifier que :**
- [ ] IPs 100-120 = Marketing
- [ ] IPs 121-150 = Développement
- [ ] IPs 151-170 = Comptabilité
- [ ] IPs 171-190 = VOIP
- [ ] IPs 201-220 = Fallback

---

**3. Les classes auto-assignent correctement**

**Test Marketing (MAC 00:11:22:XX:XX:XX) :**

```bash
# Simuler avec nmap en spécifiant une MAC
sudo nmap --script broadcast-dhcp-discover --script-args "mac=001122334455"
```

**Résultat attendu :**

```
IP Offered: 192.168.10.1XX  (dans la plage 100-120)
Domain Name: marketing.entreprise.local
```

---

**4. Les options spécifiques sont transmises**

**Pour le pool Développement, vérifier :**

- [ ] Proxy HTTP (option 252)
- [ ] Serveur TFTP (next-server)
- [ ] Fichier PXE (filename)

**Pour le pool VOIP, vérifier :**

- [ ] VLAN ID (option 132)
- [ ] Serveur SIP (option 120)

**Méthode : Capturer avec tcpdump**

```bash
sudo tcpdump -i ens33 -vvv port 67 or port 68
```

**Puis déclencher une requête DHCP.**

**Analyser les options DHCP dans la capture.**

---

**5. Durées de bail correctes**

**Vérifier dans `/var/lib/dhcp/dhcpd.leases` :**

- [ ] Marketing : 24h (86400s)
- [ ] Développement : 24h (86400s)
- [ ] Comptabilité : 12h (43200s)
- [ ] VOIP : 7-30 jours (604800-2592000s)
- [ ] Fallback : 1-2h (3600-7200s)

```bash
# Voir la durée d'un bail spécifique
cat /var/lib/dhcp/dhcpd.leases | grep -A 10 "192.168.10.105" | grep "starts\|ends"
```

**Calculer la différence entre `starts` et `ends`.**

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "hardware address conflicts with address already in use"

**Message complet :**

```
DHCPDISCOVER from 00:11:22:33:44:55 via ens33: network 192.168.10.0/24: no free leases
```

**Cause :**

L'IP réservée (ex: 192.168.10.5) est déjà utilisée par une autre machine avec une IP statique.

**Solution :**

**Vérifier qui utilise l'IP :**

```bash
ping 192.168.10.5
```

**Si réponse, quelqu'un utilise déjà cette IP !**

**Identifier la MAC :**

```bash
arp -a | grep 192.168.10.5
```

**Résultat :**

```
192.168.10.5   ether   00:99:88:77:66:55   C   ens33
```

**MAC différente de celle de la réservation (00:11:22:33:44:55) !**

**Solutions possibles :**

**1. Changer l'IP de la réservation :**

```conf
host serveur-fichiers {
    hardware ethernet 00:11:22:33:44:55;
    fixed-address 192.168.10.8;  # Nouvelle IP
}
```

**2. Libérer l'IP statique sur l'autre machine**

---

#### Erreur 2 : Les clients n'obtiennent pas le bon pool

**Symptôme :**

Un PC du marketing obtient une IP du pool Développement.

**Cause :**

La classe ne matche pas correctement.

**Débogage :**

**1. Vérifier la MAC du client :**

```bash
# Sur le client
ip link show | grep "link/ether"
```

**2. Vérifier la condition de la classe :**

```conf
class "marketing" {
    match if substring (hardware, 1, 3) = 00:11:22;
}
```

**La MAC du client commence-t-elle par `00:11:22` ?**

**Si non, modifier soit :**
- La condition de la classe
- La MAC du client (peu pratique)
- Utiliser un autre critère (hostname, vendor-class, etc.)

---

**3. Activer les logs de classe (debug) :**

```conf
# Ajouter au début du fichier
log-facility local7;
```

**Puis dans `/etc/rsyslog.conf` :**

```
local7.*    /var/log/dhcpd-debug.log
```

**Redémarrer rsyslog :**

```bash
sudo systemctl restart rsyslog
```

**Consulter :**

```bash
sudo tail -f /var/log/dhcpd-debug.log
```

---

#### Erreur 3 : Options personnalisées (proxy, SIP) non reçues

**Symptôme :**

Le client reçoit une IP mais pas l'option proxy (code 252) ou SIP (code 120).

**Causes possibles :**

**1. Option mal définie**

```conf
# [X] MAUVAIS (oubli du "option" devant le nom)
wpad-url "http://...";

# [OK] BON
option wpad-url "http://...";
```

---

**2. Option non demandée par le client**

Certains clients DHCP demandent seulement certaines options (option 55 : Parameter Request List).

**Forcer l'envoi de l'option :**

```conf
option wpad-url "http://192.168.10.50:3128/proxy.pac";

# Forcer l'envoi même si non demandée
# (Non standard, peut ne pas fonctionner partout)
```

**Vérifier avec tcpdump si l'option est envoyée :**

```bash
sudo tcpdump -i ens33 -vvv port 67 or port 68 -w dhcp.pcap
```

**Analyser avec Wireshark :**

```bash
wireshark dhcp.pcap
```

**Filtrer : `bootp`**

**Vérifier dans le paquet DHCPACK si l'option 252 est présente.**

---

**3. Client ne supporte pas l'option**

Tous les clients ne supportent pas toutes les options.

**Option 252 (WPAD) :**
- Supportée par la plupart des navigateurs modernes
- Peut nécessiter une configuration manuelle sur certains systèmes

**Option 120 (SIP) :**
- Supportée nativement par les téléphones IP
- Peut ne pas être supportée par les PC standards

---

#### Erreur 4 : Syntaxe des classes incorrecte

**Message d'erreur :**

```
/etc/dhcp/dhcpd.conf line 25: expecting a parameter or declaration
match if substring (hardware, 1, 3) = 00:11:22;
                                      ^
```

**Cause :**

Valeur hexadécimale mal formatée.

**Correct :**

```conf
match if substring (hardware, 1, 3) = 00:11:22;
```

**[ATTENTION] PAS de guillemets autour de la valeur hexa !**

**Incorrect :**

```conf
match if substring (hardware, 1, 3) = "00:11:22";  # [X]
```

---

**Pour comparer avec une chaîne de texte :**

```conf
match if substring (option host-name, 0, 4) = "dev-";  # [OK] Guillemets OK
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Réservations DHCP**
- Associent une IP fixe à une MAC
- `host <nom> { hardware ethernet XX:XX:XX:XX:XX:XX; fixed-address IP; }`
- Préférables aux IPs statiques manuelles

**2. Pools DHCP**
- `pool { range X.X.X.X X.X.X.X; }`
- Permettent de segmenter les adresses
- Chaque pool peut avoir ses propres options

**3. Classes DHCP**
- `class "nom" { match if <condition>; }`
- Identifient automatiquement les clients
- Critères : MAC, hostname, vendor-class, etc.

**4. Options DHCP personnalisées**
- `option <nom> code <numéro> = <type>;`
- Puis `option <nom> "<valeur>";`
- Exemples : proxy (252), SIP (120), VLAN ID (132)

**5. Auto-assignation**
- `allow members of "classe";` dans un pool
- `deny members of "classe";` pour exclure
- Pool fallback avec `deny` sur toutes les classes connues

**6. Bonnes pratiques**
- Documenter chaque réservation et pool
- Utiliser des conventions de nommage cohérentes
- Réserver des plages pour l'expansion future
- Tester chaque modification avant déploiement

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Options avancées par fabricant (vendor-specific)**

```conf
# Définir un espace d'options Cisco
option space cisco;
option cisco-ava-server code 1 = ip-address;
option cisco.cisco-ava-server 192.168.10.60;

# Utiliser dans un pool ou host
pool {
    range 192.168.10.171 192.168.10.190;
    vendor-option-space cisco;
}
```

---

**2. Conditions complexes avec opérateurs**

```conf
class "dev-linux" {
    # ET logique
    match if (substring (option host-name, 0, 4) = "dev-")
          and (substring (option vendor-class-identifier, 0, 5) = "Linux");
}

class "dev-ou-marketing" {
    # OU logique
    match if (substring (hardware, 1, 3) = 00:11:22)
          or (substring (option host-name, 0, 4) = "dev-");
}
```

---

**3. Gestion dynamique des baux (relâchement forcé)**

```bash
# Relâcher un bail spécifique
sudo dhcp_release <interface> <ip> <mac>

# Exemple
sudo dhcp_release ens33 192.168.10.100 00:11:22:33:44:55
```

**Utile pour forcer un client à obtenir une nouvelle IP.**

---

**4. Statistiques par pool**

**Script pour compter les attributions par pool :**

```bash
#!/bin/bash

echo "=== Statistiques DHCP par pool ==="
echo ""

echo "Marketing (100-120) :"
grep "binding state active" /var/lib/dhcp/dhcpd.leases -B 5 | \
grep "lease 192.168.10.1[0-1][0-9]\|lease 192.168.10.120" | wc -l

echo "Développement (121-150) :"
grep "binding state active" /var/lib/dhcp/dhcpd.leases -B 5 | \
grep "lease 192.168.10.1[2-4][0-9]\|lease 192.168.10.150" | wc -l

# etc.
```

---

**5. Migration progressive d'un pool à l'autre**

**Exemple : Migrer le marketing de 100-120 vers 50-70**

```conf
# Étape 1 : Ajouter le nouveau pool
pool {
    allow members of "marketing";
    range 192.168.10.50 192.168.10.70;
    # Nouvelles options...
}

# Étape 2 : Garder l'ancien pool (durée de bail courte)
pool {
    allow members of "marketing";
    range 192.168.10.100 192.168.10.120;
    max-lease-time 3600;  # 1 heure
}

# Étape 3 : Attendre que les baux expirent (quelques jours)

# Étape 4 : Supprimer l'ancien pool
```

---

## [COURS] CONCLUSION DE L'EXERCICE 2

**[OK] Félicitations ! Tu maîtrises maintenant les réservations DHCP et les options avancées !**

**Ce que tu as appris :**
- Créer des réservations DHCP basées sur les adresses MAC
- Configurer plusieurs pools dans un même subnet
- Utiliser les classes DHCP pour auto-assigner les clients
- Définir des options DHCP personnalisées
- Configurer des options spécifiques (proxy, TFTP, VOIP)
- Organiser une architecture DHCP multi-départements
- Documenter une configuration complexe

**Compétences acquises :**
- [OK] Réservations DHCP (niveau intermédiaire-avancé)
- [OK] Pools et segmentation
- [OK] Classes et auto-assignation
- [OK] Options DHCP avancées
- [OK] Architecture réseau d'entreprise

**Temps moyen de réalisation :** 2-3 heures

**Prochaine étape :** Exercice 3 - Intégration DHCP + DNS dynamique (DDNS) ! [WEB]

---

Je continue avec l'exercice 3 ?

# [JAUNE] EXERCICE 3 : INTÉGRATION DHCP + DNS DYNAMIQUE (DDNS)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu continues ton travail d'administrateur réseau dans la PME. Le responsable IT constate un problème : avec le DHCP en place, les adresses IP changent régulièrement et il est difficile de :
- Accéder aux machines par leur nom (ex: `ping srv-fichiers`)
- Maintenir des enregistrements DNS à jour manuellement
- Gérer les 50+ postes de travail qui changent d'IP

**Solution demandée :** Mettre en place le DNS dynamique (DDNS) pour que :
1. Quand une machine obtient une IP via DHCP, son nom DNS est automatiquement créé
2. Quand le bail expire, l'enregistrement DNS est supprimé
3. Les mises à jour sont sécurisées (pas n'importe qui ne peut modifier le DNS)

### Cahier des charges

**Infrastructure cible :**
```
Serveur DHCP + DNS (même machine)
└─ IP : 192.168.10.10

Zones DNS à créer :
├─ Zone forward : entreprise.local
│  └─ Enregistrements A : nom -> IP
└─ Zone reverse : 10.168.192.in-addr.arpa
   └─ Enregistrements PTR : IP -> nom

Intégration DHCP <-> DNS :
└─ Mise à jour automatique et sécurisée avec TSIG
```

**Scénarios de test :**
1. Client "pc-alice" obtient 192.168.10.105 -> DNS créé automatiquement
2. `ping pc-alice.entreprise.local` -> Fonctionne
3. `nslookup 192.168.10.105` -> Retourne "pc-alice.entreprise.local"
4. Le bail expire -> Enregistrement DNS supprimé automatiquement

### Contraintes techniques

- Serveur DNS : BIND9 (Berkeley Internet Name Domain)
- Sécurisation : TSIG (Transaction Signature) avec clé partagée
- Zones DNS dynamiques (allow-update)
- Temps estimé : 2-3 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Installer et configurer BIND9 (serveur DNS)
- [OK] Créer des zones DNS forward et reverse
- [OK] Comprendre les enregistrements DNS (A, PTR, SOA, NS)
- [OK] Générer une clé TSIG pour sécuriser les mises à jour
- [OK] Configurer le DDNS dans ISC DHCP Server
- [OK] Intégrer DHCP et DNS avec mise à jour automatique
- [OK] Tester la résolution DNS directe et inverse
- [OK] Déboguer les problèmes DDNS courants

---

## [DOCS] PRÉREQUIS

- Exercices 1 et 2 terminés avec succès
- Serveur DHCP fonctionnel
- Compréhension basique du DNS
- Connaissance des enregistrements DNS de base

---

## [IDEE] COMPRENDRE DNS ET DDNS

### Qu'est-ce que le DNS ?

**DNS** = Domain Name System (Système de Noms de Domaine)

**Rôle** : Traduire les noms en adresses IP et vice-versa

**Analogie :**
- DNS = Annuaire téléphonique
- Nom = "Jean Dupont"
- IP = "06 12 34 56 78"

**Sans DNS :**
```bash
ping 192.168.10.105  # Difficile à retenir !
```

**Avec DNS :**
```bash
ping pc-alice.entreprise.local  # Facile à retenir !
```

---

### Résolution DNS directe (Forward)

**Nom -> IP**

```
Question : Quelle est l'IP de "pc-alice.entreprise.local" ?
Réponse : 192.168.10.105
```

**Enregistrement DNS utilisé : A (Address)**

```
pc-alice.entreprise.local.    IN    A    192.168.10.105
```

---

### Résolution DNS inverse (Reverse)

**IP -> Nom**

```
Question : Quel est le nom de "192.168.10.105" ?
Réponse : pc-alice.entreprise.local
```

**Enregistrement DNS utilisé : PTR (Pointer)**

```
105.10.168.192.in-addr.arpa.    IN    PTR    pc-alice.entreprise.local.
```

**[ATTENTION] L'IP est inversée dans la zone reverse !**

**192.168.10.105 devient 105.10.168.192.in-addr.arpa**

**Pourquoi ?**
- Convention DNS pour les zones reverse
- Permet une hiérarchie cohérente
- 192.in-addr.arpa -> 168.192.in-addr.arpa -> 10.168.192.in-addr.arpa

---

### Qu'est-ce que le DDNS ?

**DDNS** = Dynamic DNS (DNS Dynamique)

**Rôle** : Mise à jour automatique des enregistrements DNS

**Sans DDNS (configuration manuelle) :**
```
1. Alice obtient l'IP 192.168.10.105 via DHCP
2. L'admin doit MANUELLEMENT ajouter dans le DNS :
   pc-alice.entreprise.local -> 192.168.10.105
3. Alice libère son IP
4. L'admin doit MANUELLEMENT supprimer l'enregistrement
```

**Chronophage, source d'erreurs, ne passe pas à l'échelle !**

---

**Avec DDNS (automatique) :**
```
1. Alice obtient l'IP 192.168.10.105 via DHCP
2. Le serveur DHCP contacte AUTOMATIQUEMENT le serveur DNS
3. Le serveur DNS ajoute AUTOMATIQUEMENT :
   pc-alice.entreprise.local -> 192.168.10.105
4. Alice libère son IP
5. Le serveur DHCP contacte le serveur DNS
6. Le serveur DNS supprime AUTOMATIQUEMENT l'enregistrement
```

**Automatique, fiable, scalable ! [OK]**

---

### Comment fonctionne le DDNS ?

```
┌─────────────┐                    ┌──────────────┐
│   Client    │                    │ Serveur DHCP │
│  (Alice)    │                    │ 192.168.10.10│
└─────────────┘                    └──────────────┘
       │                                   │
       │  1. DHCPDISCOVER                  │
       │──────────────────────────────────>│
       │                                   │
       │  2. DHCPOFFER (192.168.10.105)    │
       │<──────────────────────────────────│
       │                                   │
       │  3. DHCPREQUEST                   │
       │──────────────────────────────────>│
       │                                   │
       │  4. DHCPACK                       │
       │<──────────────────────────────────│
       │                                   │
       │                                   │  5. DNS Update
       │                                   │     (pc-alice -> 105)
       │                                   │──────────────────┐
       │                                   │                  │
       │                                   │                  [BLACK_DOWN-POINTING_TRIANGLE]
       │                                   │          ┌──────────────┐
       │                                   │          │ Serveur DNS  │
       │                                   │          │ 192.168.10.10│
       │                                   │          └──────────────┘
       │                                   │                  │
       │                                   │  6. DNS Update OK│
       │                                   │<─────────────────┘
```

**Étapes :**

1-4. **Processus DHCP normal** (DORA)

5. **DHCP contacte DNS :**
   - Envoie une requête DNS UPDATE
   - Contient : nom (pc-alice), IP (192.168.10.105)
   - Authentifié avec TSIG (clé secrète partagée)

6. **DNS valide et applique :**
   - Vérifie la signature TSIG
   - Ajoute l'enregistrement A (forward)
   - Ajoute l'enregistrement PTR (reverse)

---

### Qu'est-ce que TSIG ?

**TSIG** = Transaction SIGnature

**Rôle** : Sécuriser les communications DNS avec authentification

**Analogie :**
- TSIG = Signature électronique + sceau
- Clé partagée = Secret connu uniquement par DHCP et DNS
- Empêche les mises à jour DNS non autorisées

**Sans TSIG :**
```
N'importe qui peut envoyer : "Ajoute google.com -> 192.168.10.1"
-> Attaque possible ! [X]
```

**Avec TSIG :**
```
Seuls ceux qui connaissent la clé secrète peuvent mettre à jour
-> Sécurisé [OK]
```

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Installer BIND9 (serveur DNS)

```bash
# Mettre à jour les paquets
sudo apt update

# Installer BIND9
sudo apt install bind9 bind9utils bind9-doc -y
```

**Paquets installés :**

**bind9**
- Serveur DNS principal
- Daemon : `named` (Name Daemon)

**bind9utils**
- Outils de gestion : `rndc`, `dig`, `nslookup`
- `rndc` = Remote Name Daemon Control (contrôle à distance de BIND)

**bind9-doc**
- Documentation complète
- Fichiers dans `/usr/share/doc/bind9/`

---

**Vérifier l'installation :**

```bash
named -v
```

**Résultat :**

```
BIND 9.18.12-0ubuntu0.22.04.3 (Extended Support Version)
```

**[OK] BIND9 installé !**

---

**Vérifier que le service tourne :**

```bash
sudo systemctl status bind9
```

**Résultat attendu :**

```
[BLACK_CIRCLE] named.service - BIND Domain Name Server
     Loaded: loaded (/lib/systemd/system/named.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2024-12-16 22:00:00 UTC; 1min ago
       Docs: man:named(8)
   Main PID: 23456 (named)
      Tasks: 4 (limit: 4915)
     Memory: 15.2M
        CPU: 234ms
```

**[OK] BIND9 actif et en cours d'exécution !**

---

### ÉTAPE 2 : Générer une clé TSIG

**La clé TSIG permet au serveur DHCP de s'authentifier auprès du serveur DNS.**

**Générer une clé TSIG avec `tsig-keygen` :**

```bash
sudo tsig-keygen -a HMAC-SHA256 ddns-key > /etc/bind/ddns.key
```

**Explication de la commande :**

**`tsig-keygen`**
- Utilitaire pour générer des clés TSIG
- Fourni par bind9utils

**`-a HMAC-SHA256`**
- Algorithme de hachage cryptographique
- HMAC-SHA256 = Hash-based Message Authentication Code avec SHA-256
- Alternatives : HMAC-MD5 (obsolète), HMAC-SHA512

**`ddns-key`**
- Nom de la clé (identifiant)
- Peut être n'importe quoi (alphanumeric)

**`> /etc/bind/ddns.key`**
- Redirige la sortie vers le fichier `/etc/bind/ddns.key`

---

**Examiner le contenu de la clé générée :**

```bash
sudo cat /etc/bind/ddns.key
```

**Résultat (exemple) :**

```
key "ddns-key" {
	algorithm hmac-sha256;
	secret "vJZ3fK8mN2pQ5rT7wX9yA1bC3dE4fG6hI8jK0lM2nO4=";
};
```

**Explication :**

**`key "ddns-key" { ... }`**
- Bloc de définition de la clé
- "ddns-key" = Nom de la clé

**`algorithm hmac-sha256;`**
- Algorithme utilisé pour signer

**`secret "vJZ3...";`**
- Clé secrète en base64
- 256 bits = 32 octets = 44 caractères en base64
- [ATTENTION] À garder ABSOLUMENT SECRET !

---

**Sécuriser le fichier de clé :**

```bash
# Propriétaire : root et bind
sudo chown root:bind /etc/bind/ddns.key

# Permissions : Lecture seule pour root et bind
sudo chmod 640 /etc/bind/ddns.key
```

**Permissions 640 :**
- `root` : rw- (lecture/écriture)
- `bind` : r-- (lecture seule)
- `others` : --- (aucun accès)

**[ATTENTION] Si quelqu'un obtient cette clé, il peut modifier ton DNS !**

---

### ÉTAPE 3 : Configurer BIND9 (serveur DNS)

**Sauvegarder la configuration par défaut :**

```bash
sudo cp /etc/bind/named.conf.local /etc/bind/named.conf.local.backup
sudo cp /etc/bind/named.conf.options /etc/bind/named.conf.options.backup
```

---

#### 3.1 - Configurer les options globales de BIND

```bash
sudo nano /etc/bind/named.conf.options
```

**Remplacer le contenu par :**

```conf
// ═══════════════════════════════════════════════════════════════
// CONFIGURATION OPTIONS GLOBALES DE BIND9
// ═══════════════════════════════════════════════════════════════

options {
    // Répertoire de travail de BIND
    directory "/var/cache/bind";
    
    // Explication :
    // directory = Dossier où BIND stocke ses fichiers de travail
    // - Fichiers de zones
    // - Fichiers de cache
    // - Fichiers de journalisation
    //
    // /var/cache/bind = Emplacement standard sur Ubuntu/Debian
    
    // ───────────────────────────────────────────────────────────
    // FORWARDERS (REDIRECTEURS DNS)
    // ───────────────────────────────────────────────────────────
    
    // Serveurs DNS à interroger pour les domaines externes
    forwarders {
        8.8.8.8;
        8.8.4.4;
    };
    
    // Explication :
    // forwarders = Liste de serveurs DNS externes
    //
    // Quand BIND reçoit une requête pour un domaine qu'il ne gère pas :
    // Exemple : google.com (pas dans nos zones)
    //
    // SANS forwarders :
    // BIND contacte les serveurs racine (root servers)
    // Puis .com, puis google.com (récursion complète)
    //
    // AVEC forwarders :
    // BIND demande à 8.8.8.8 (Google DNS)
    // Plus rapide, utilise le cache de Google
    //
    // 8.8.8.8 = Google DNS primaire
    // 8.8.4.4 = Google DNS secondaire
    //
    // Alternatives :
    // 1.1.1.1, 1.0.0.1 (Cloudflare)
    // 9.9.9.9 (Quad9)
    
    // ───────────────────────────────────────────────────────────
    // DNSSEC (SÉCURISATION DNS)
    // ───────────────────────────────────────────────────────────
    
    // Validation DNSSEC
    dnssec-validation auto;
    
    // Explication :
    // DNSSEC = DNS Security Extensions
    // Valide que les réponses DNS n'ont pas été falsifiées
    //
    // auto = Active la validation automatique
    // yes = Force la validation
    // no = Désactive
    //
    // Pour cet exercice (réseau interne), auto suffit
    
    // ───────────────────────────────────────────────────────────
    // ÉCOUTE SUR TOUTES LES INTERFACES
    // ───────────────────────────────────────────────────────────
    
    // Interfaces d'écoute (IPv4)
    listen-on { any; };
    
    // Explication :
    // listen-on = Interfaces sur lesquelles BIND écoute
    //
    // any = Toutes les interfaces
    // Équivalent à : 0.0.0.0 (toutes les IPs)
    //
    // Alternative (restreindre) :
    // listen-on { 192.168.10.10; 127.0.0.1; };
    // Écoute seulement sur 192.168.10.10 et localhost
    //
    // Pourquoi restreindre en production ?
    // - Sécurité (éviter exposition WAN)
    // - Performance (moins de trafic à gérer)
    
    // Interfaces d'écoute (IPv6) - Désactivé pour cet exercice
    listen-on-v6 { none; };
    
    // Explication :
    // none = Désactive IPv6
    // any = Active IPv6 sur toutes les interfaces
    // ::1 = Localhost IPv6 seulement
    
    // ───────────────────────────────────────────────────────────
    // CONTRÔLE D'ACCÈS (ACL)
    // ───────────────────────────────────────────────────────────
    
    // Autoriser les requêtes récursives depuis le réseau local
    allow-recursion { 
        localhost;           // 127.0.0.1
        192.168.10.0/24;     // Réseau local
    };
    
    // Explication :
    // allow-recursion = Qui peut demander une résolution récursive
    //
    // Résolution récursive :
    // Client demande : "Quelle est l'IP de google.com ?"
    // BIND fait TOUT le travail :
    // 1. Interroge les root servers
    // 2. Interroge les serveurs .com
    // 3. Interroge les serveurs google.com
    // 4. Retourne l'IP au client
    //
    // Résolution non-récursive :
    // BIND répond seulement s'il connaît directement
    // Sinon, indique au client d'interroger un autre serveur
    //
    // Pourquoi limiter la récursion ?
    // - Éviter que ton serveur DNS soit utilisé pour des attaques DDoS
    // - Économiser les ressources
    // - Sécurité
    //
    // localhost = Le serveur lui-même
    // 192.168.10.0/24 = Réseau local
    //
    // En production, TOUJOURS limiter !
    // allow-recursion { none; }; pour un serveur public
    
    // Autoriser les requêtes depuis le réseau local
    allow-query { 
        localhost; 
        192.168.10.0/24; 
    };
    
    // Explication :
    // allow-query = Qui peut interroger ce serveur DNS
    //
    // Plus large que allow-recursion :
    // - allow-query = Toutes les requêtes DNS
    // - allow-recursion = Seulement les requêtes récursives
    //
    // Exemple :
    // Client demande : "Quelle est l'IP de pc-alice.entreprise.local ?"
    // C'est notre zone, pas besoin de récursion
    // allow-query suffit
};

// ═══════════════════════════════════════════════════════════════
```

**Sauvegarde : `Ctrl + O`, `Entrée`, `Ctrl + X`**

---

#### 3.2 - Configurer les zones DNS locales

```bash
sudo nano /etc/bind/named.conf.local
```

**Ajouter :**

```conf
// ═══════════════════════════════════════════════════════════════
// CONFIGURATION DES ZONES DNS LOCALES
// ═══════════════════════════════════════════════════════════════

// Inclure la clé TSIG
include "/etc/bind/ddns.key";

// Explication :
// include = Inclut le contenu d'un autre fichier
// 
// ddns.key contient :
// key "ddns-key" { algorithm ...; secret "..."; };
//
// Rend la clé disponible dans toute la configuration

// ───────────────────────────────────────────────────────────────
// ZONE FORWARD (NOM -> IP)
// ───────────────────────────────────────────────────────────────

zone "entreprise.local" {
    // Type de zone
    type master;
    
    // Explication :
    // type = Type de serveur DNS pour cette zone
    //
    // master (ou primary) :
    // - Serveur DNS autoritaire principal
    // - Contient la version originale de la zone
    // - Peut accepter des mises à jour
    //
    // slave (ou secondary) :
    // - Serveur DNS autoritaire secondaire
    // - Copie la zone depuis le master
    // - Lecture seule (ne peut pas modifier)
    // - Haute disponibilité (si master down)
    //
    // forward :
    // - Redirige toutes les requêtes vers d'autres serveurs
    //
    // hint :
    // - Zone racine (root servers)
    
    // Fichier de la zone
    file "/var/lib/bind/db.entreprise.local";
    
    // Explication :
    // file = Chemin du fichier de zone
    //
    // /var/lib/bind/ (et NON /etc/bind/) :
    // - Zones dynamiques (modifiées par DDNS)
    // - BIND doit pouvoir ÉCRIRE dans ce fichier
    // - /etc/bind/ = Lecture seule
    // - /var/lib/bind/ = Lecture/écriture
    //
    // db.entreprise.local = Nom du fichier
    // Convention : db.<nom-de-zone>
    
    // Autoriser les mises à jour dynamiques avec la clé TSIG
    allow-update { key "ddns-key"; };
    
    // Explication :
    // allow-update = Qui peut mettre à jour cette zone
    //
    // { key "ddns-key"; } :
    // Seulement les requêtes signées avec la clé "ddns-key"
    //
    // Autres options :
    // allow-update { none; };
    // - Aucune mise à jour (zone statique)
    //
    // allow-update { 192.168.10.10; };
    // - Autorise les mises à jour depuis cette IP
    // - DANGEREUX (pas d'authentification)
    //
    // allow-update { any; };
    // - Autorise n'importe qui
    // - TRÈS DANGEREUX ! À ne JAMAIS utiliser
    //
    // TOUJOURS utiliser TSIG en production !
};

// ───────────────────────────────────────────────────────────────
// ZONE REVERSE (IP -> NOM)
// ───────────────────────────────────────────────────────────────

zone "10.168.192.in-addr.arpa" {
    // Type de zone
    type master;
    
    // Explication du nom de zone :
    // 10.168.192.in-addr.arpa
    //
    // Pour le réseau 192.168.10.0/24 :
    // - Prendre les 3 premiers octets : 192.168.10
    // - Les inverser : 10.168.192
    // - Ajouter .in-addr.arpa
    // - Résultat : 10.168.192.in-addr.arpa
    //
    // Pourquoi inverser ?
    // - Hiérarchie DNS (comme les domaines)
    // - 192.in-addr.arpa contient 192.*.*.*
    // - 168.192.in-addr.arpa contient 192.168.*.*
    // - 10.168.192.in-addr.arpa contient 192.168.10.*
    //
    // Exemples :
    // Réseau 10.0.0.0/8 -> 10.in-addr.arpa
    // Réseau 172.16.0.0/16 -> 16.172.in-addr.arpa
    // Réseau 192.168.1.0/24 -> 1.168.192.in-addr.arpa
    
    // Fichier de la zone reverse
    file "/var/lib/bind/db.192.168.10";
    
    // Explication :
    // Convention de nommage : db.<réseau-normal>
    // Plus facile à identifier qu'avec le nom inversé
    
    // Autoriser les mises à jour dynamiques
    allow-update { key "ddns-key"; };
};

// ═══════════════════════════════════════════════════════════════
```

**Sauvegarde.**

---

### ÉTAPE 4 : Créer les fichiers de zones DNS

#### 4.1 - Créer le dossier pour les zones dynamiques

```bash
# Créer le dossier
sudo mkdir -p /var/lib/bind

# Donner les bonnes permissions
sudo chown bind:bind /var/lib/bind
sudo chmod 775 /var/lib/bind
```

**Explication :**

**`/var/lib/bind`**
- Dossier pour les zones dynamiques (modifiables par BIND)
- Différent de `/etc/bind` (fichiers de config statiques)

**`chown bind:bind`**
- Propriétaire : utilisateur `bind`
- Groupe : groupe `bind`
- BIND tourne sous l'utilisateur `bind`

**`chmod 775`**
- Permissions : rwxrwxr-x
- Propriétaire (bind) : rwx (lecture/écriture/exécution)
- Groupe (bind) : rwx
- Autres : r-x (lecture/exécution)

---

#### 4.2 - Créer le fichier de zone forward

```bash
sudo nano /var/lib/bind/db.entreprise.local
```

**Contenu :**

```zone
; ═══════════════════════════════════════════════════════════════
; ZONE FORWARD : entreprise.local
; ═══════════════════════════════════════════════════════════════
; Description : Zone DNS forward (Nom -> IP)
; Mise à jour : Dynamique (DDNS via DHCP)
; ═══════════════════════════════════════════════════════════════

; ───────────────────────────────────────────────────────────────
; ENREGISTREMENT SOA (START OF AUTHORITY)
; ───────────────────────────────────────────────────────────────

$TTL 86400
@    IN    SOA    ns1.entreprise.local. admin.entreprise.local. (
            2024121601    ; Serial (YYYYMMDDNN)
            3600          ; Refresh (1 heure)
            1800          ; Retry (30 minutes)
            604800        ; Expire (7 jours)
            86400         ; Minimum TTL (24 heures)
)

; Explication ligne par ligne :
;
; $TTL 86400
; - TTL = Time To Live (durée de vie)
; - 86400 secondes = 24 heures
; - Durée pendant laquelle les clients peuvent cacher cet enregistrement
; - $TTL s'applique à TOUS les enregistrements suivants
;
; @    IN    SOA    ns1.entreprise.local. admin.entreprise.local. (
; - @ = Nom de la zone (entreprise.local)
; - IN = Internet (classe de réseau, toujours IN)
; - SOA = Start Of Authority (enregistrement d'autorité)
; - ns1.entreprise.local. = Serveur DNS primaire pour cette zone
;   [ATTENTION] Point final obligatoire ! (FQDN absolu)
; - admin.entreprise.local. = Email de l'admin (remplace @ par .)
;   Équivalent à : admin@entreprise.local
;
; 2024121601    ; Serial
; - Numéro de série de la zone
; - Format recommandé : YYYYMMDDNN
;   YYYY = Année (2024)
;   MM = Mois (12)
;   DD = Jour (16)
;   NN = Numéro de révision du jour (01, 02, ...)
; - DOIT être incrémenté à chaque modification
; - Les serveurs secondaires (slaves) l'utilisent pour détecter les mises à jour
; - Si Serial nouveau > Serial ancien -> Transfert de zone (AXFR)
;
; 3600          ; Refresh
; - Intervalle de rafraîchissement (1 heure)
; - Fréquence à laquelle un serveur secondaire vérifie les mises à jour
; - Secondaire interroge le primaire toutes les heures
;
; 1800          ; Retry
; - Intervalle de nouvelle tentative (30 minutes)
; - Si le refresh échoue, réessayer après 30 minutes
;
; 604800        ; Expire
; - Délai d'expiration (7 jours)
; - Si le secondaire ne peut pas contacter le primaire pendant 7 jours,
;   il arrête de répondre pour cette zone (considère les données obsolètes)
;
; 86400         ; Minimum TTL
; - TTL minimum pour les enregistrements de cette zone (24 heures)
; - Utilisé aussi pour les réponses NXDOMAIN (domaine inexistant)
; - Durée pendant laquelle un client cache "ce domaine n'existe pas"

; ───────────────────────────────────────────────────────────────
; ENREGISTREMENTS NS (NAME SERVER)
; ───────────────────────────────────────────────────────────────

@    IN    NS    ns1.entreprise.local.

; Explication :
; @ = entreprise.local (nom de la zone)
; IN = Internet
; NS = Name Server (serveur de noms)
; ns1.entreprise.local. = Serveur DNS autoritaire pour cette zone
;
; Cet enregistrement déclare :
; "Le serveur DNS autoritaire pour entreprise.local est ns1.entreprise.local"
;
; Si plusieurs serveurs DNS (haute disponibilité) :
; @    IN    NS    ns1.entreprise.local.
; @    IN    NS    ns2.entreprise.local.
;
; Les clients interrogeront ns1, et si down, basculement vers ns2

; ───────────────────────────────────────────────────────────────
; ENREGISTREMENT A (ADDRESS) POUR LE SERVEUR DNS
; ───────────────────────────────────────────────────────────────

ns1    IN    A    192.168.10.10

; Explication :
; ns1 = Nom d'hôte (relatif à la zone)
; Nom complet (FQDN) = ns1.entreprise.local
;
; IN = Internet
; A = Address (IPv4)
; 192.168.10.10 = Adresse IP du serveur DNS
;
; Cet enregistrement déclare :
; "ns1.entreprise.local a l'adresse IP 192.168.10.10"
;
; Nécessaire car l'enregistrement NS référence ns1.entreprise.local
; Il faut donc un enregistrement A pour résoudre ce nom

; ───────────────────────────────────────────────────────────────
; ENREGISTREMENTS A STATIQUES (RÉSERVATIONS DHCP)
; ───────────────────────────────────────────────────────────────

; Serveur de fichiers (réservation DHCP)
srv-fichiers    IN    A    192.168.10.5

; Imprimante RH (réservation DHCP)
print-rh        IN    A    192.168.10.6

; Serveur web (réservation DHCP)
srv-web         IN    A    192.168.10.7

; Explication :
; Ces enregistrements sont STATIQUES
; Pourquoi ne pas les laisser au DDNS ?
;
; Réservations DHCP = IP TOUJOURS la même
; -> On peut les mettre en dur dans le DNS
;
; Avantage :
; - Ces entrées persistent même si DHCP/DDNS down
; - Documentation claire de l'infrastructure
;
; DDNS ajoutera automatiquement les autres machines (postes de travail)

; ───────────────────────────────────────────────────────────────
; ENREGISTREMENTS CNAME (ALIAS) - OPTIONNELS
; ───────────────────────────────────────────────────────────────

; Alias pour les services
www             IN    CNAME    srv-web
files           IN    CNAME    srv-fichiers
printer         IN    CNAME    print-rh

; Explication :
; CNAME = Canonical Name (nom canonique = alias)
;
; www.entreprise.local -> srv-web.entreprise.local -> 192.168.10.7
; files.entreprise.local -> srv-fichiers.entreprise.local -> 192.168.10.5
;
; Processus de résolution :
; 1. Client demande : www.entreprise.local
; 2. DNS répond : CNAME srv-web.entreprise.local
; 3. Client redemande : srv-web.entreprise.local
; 4. DNS répond : A 192.168.10.7
;
; Avantage :
; - Si l'IP du serveur web change, modifier 1 seul enregistrement A
; - Les CNAME continuent de fonctionner
;
; [ATTENTION] Limitation :
; Un CNAME ne peut pas coexister avec d'autres enregistrements
; Correct : www IN CNAME srv-web
; Incorrect : www IN CNAME srv-web
;             www IN A 192.168.10.7
; (Interdit d'avoir CNAME + autre chose pour le même nom)

; ───────────────────────────────────────────────────────────────
; ENREGISTREMENTS MX (MAIL EXCHANGER) - OPTIONNELS
; ───────────────────────────────────────────────────────────────

; @    IN    MX    10    mail.entreprise.local.
; mail IN    A     192.168.10.20

; Explication :
; MX = Mail eXchanger (serveur de messagerie)
;
; @ = entreprise.local
; 10 = Priorité (plus c'est bas, plus c'est prioritaire)
; mail.entreprise.local. = Serveur de mail
;
; Si email vers : alice@entreprise.local
; Le serveur de mail expéditeur :
; 1. Cherche MX pour entreprise.local
; 2. Trouve mail.entreprise.local (priorité 10)
; 3. Résout mail.entreprise.local -> 192.168.10.20
; 4. Envoie l'email à 192.168.10.20
;
; Plusieurs MX (redondance) :
; @    IN    MX    10    mail1.entreprise.local.
; @    IN    MX    20    mail2.entreprise.local.
; Si mail1 down, utilise mail2

; ═══════════════════════════════════════════════════════════════
; FIN DE LA ZONE FORWARD
; ═══════════════════════════════════════════════════════════════
; Les enregistrements DDNS seront ajoutés automatiquement ci-dessous
; par le serveur DNS lors des mises à jour dynamiques
```

**Sauvegarde.**

---

#### 4.3 - Créer le fichier de zone reverse

```bash
sudo nano /var/lib/bind/db.192.168.10
```

**Contenu :**

```zone
; ═══════════════════════════════════════════════════════════════
; ZONE REVERSE : 10.168.192.in-addr.arpa
; ═══════════════════════════════════════════════════════════════
; Description : Zone DNS reverse (IP -> Nom)
; Réseau : 192.168.10.0/24
; Mise à jour : Dynamique (DDNS via DHCP)
; ═══════════════════════════════════════════════════════════════

$TTL 86400
@    IN    SOA    ns1.entreprise.local. admin.entreprise.local. (
            2024121601    ; Serial
            3600          ; Refresh
            1800          ; Retry
            604800        ; Expire
            86400         ; Minimum TTL
)

; Serveur de noms pour cette zone reverse
@    IN    NS    ns1.entreprise.local.

; ───────────────────────────────────────────────────────────────
; ENREGISTREMENTS PTR (POINTER) STATIQUES
; ───────────────────────────────────────────────────────────────

; Serveur DNS lui-même
10    IN    PTR    ns1.entreprise.local.

; Explication :
; 10 = Dernier octet de l'IP (192.168.10.10)
; PTR = Pointer (pointeur)
; ns1.entreprise.local. = Nom complet (FQDN)
;
; Résolution complète :
; Requête : "Quel est le nom de 192.168.10.10 ?"
; Zone : 10.168.192.in-addr.arpa
; Enregistrement : 10.10.168.192.in-addr.arpa PTR ns1.entreprise.local.
;                  ^
;                  Simplifié en "10" dans le fichier de zone
;
; Résultat : ns1.entreprise.local

; Réservations DHCP
5     IN    PTR    srv-fichiers.entreprise.local.
6     IN    PTR    print-rh.entreprise.local.
7     IN    PTR    srv-web.entreprise.local.

; Explication :
; 5 = 192.168.10.5 -> srv-fichiers.entreprise.local
; 6 = 192.168.10.6 -> print-rh.entreprise.local
; 7 = 192.168.10.7 -> srv-web.entreprise.local
;
; Cohérence avec la zone forward :
; Zone forward : srv-fichiers A 192.168.10.5
; Zone reverse : 5 PTR srv-fichiers.entreprise.local
;
; Les deux DOIVENT correspondre !
; Sinon :
; - Problèmes de résolution inverse
; - Échecs de validation (certains services vérifient la cohérence)
; - Logs d'erreur

; ═══════════════════════════════════════════════════════════════
; FIN DE LA ZONE REVERSE
; ═══════════════════════════════════════════════════════════════
; Les enregistrements PTR dynamiques seront ajoutés automatiquement
```

**Sauvegarde.**

---

#### 4.4 - Définir les bonnes permissions

```bash
# Changer le propriétaire des fichiers de zones
sudo chown bind:bind /var/lib/bind/db.entreprise.local
sudo chown bind:bind /var/lib/bind/db.192.168.10

# Définir les permissions (lecture/écriture pour bind)
sudo chmod 644 /var/lib/bind/db.entreprise.local
sudo chmod 644 /var/lib/bind/db.192.168.10
```

**Explication :**

**Propriétaire : `bind:bind`**
- L'utilisateur `bind` doit pouvoir modifier ces fichiers
- DDNS va ajouter/supprimer des enregistrements

**Permissions : `644`**
- Propriétaire (bind) : rw- (lecture/écriture)
- Groupe (bind) : r-- (lecture)
- Autres : r-- (lecture)

---

### ÉTAPE 5 : Vérifier et démarrer BIND9

**Vérifier la syntaxe de la configuration :**

```bash
sudo named-checkconf
```

**Si aucune erreur n'est affichée, la configuration est valide [OK]**

**Si erreur :**

```
/etc/bind/named.conf.local:10: missing ';' before 'zone'
```

**Message clair indiquant le fichier, la ligne et l'erreur.**

---

**Vérifier la syntaxe des zones :**

```bash
# Zone forward
sudo named-checkzone entreprise.local /var/lib/bind/db.entreprise.local
```

**Résultat attendu :**

```
zone entreprise.local/IN: loaded serial 2024121601
OK
```

**[OK] Zone forward valide !**

---

```bash
# Zone reverse
sudo named-checkzone 10.168.192.in-addr.arpa /var/lib/bind/db.192.168.10
```

**Résultat attendu :**

```
zone 10.168.192.in-addr.arpa/IN: loaded serial 2024121601
OK
```

**[OK] Zone reverse valide !**

---

**Redémarrer BIND9 :**

```bash
sudo systemctl restart bind9
```

**Vérifier l'état :**

```bash
sudo systemctl status bind9
```

**Résultat attendu :**

```
[BLACK_CIRCLE] named.service - BIND Domain Name Server
     Loaded: loaded
     Active: active (running) since Mon 2024-12-16 22:30:00 UTC; 10s ago
```

**[OK] BIND9 actif !**

---

**Vérifier que BIND écoute sur le port 53 :**

```bash
sudo netstat -tulpn | grep named
```

**Résultat :**

```
tcp    0    0 192.168.10.10:53    0.0.0.0:*    LISTEN    23456/named
tcp    0    0 127.0.0.1:53        0.0.0.0:*    LISTEN    23456/named
udp    0    0 192.168.10.10:53    0.0.0.0:*              23456/named
udp    0    0 127.0.0.1:53        0.0.0.0:*              23456/named
```

**[OK] BIND écoute sur le port 53 (UDP et TCP) !**

---

### ÉTAPE 6 : Tester le DNS (avant DDNS)

**Tester la résolution des enregistrements statiques :**

```bash
# Tester avec dig (recommandé)
dig @192.168.10.10 ns1.entreprise.local
```

**Résultat attendu :**

```
; <<>> DiG 9.18.12 <<>> @192.168.10.10 ns1.entreprise.local
;; QUESTION SECTION:
;ns1.entreprise.local.    IN    A

;; ANSWER SECTION:
ns1.entreprise.local.    86400    IN    A    192.168.10.10

;; Query time: 1 msec
;; SERVER: 192.168.10.10#53(192.168.10.10)
;; WHEN: Mon Dec 16 22:35:00 UTC 2024
;; MSG SIZE  rcvd: 53
```

**Points importants :**

**`ANSWER SECTION`**
- `ns1.entreprise.local.    86400    IN    A    192.168.10.10`
- [OK] Résolution fonctionne !

**`Query time: 1 msec`**
- [OK] Rapide (serveur local)

---

**Tester d'autres enregistrements :**

```bash
dig @192.168.10.10 srv-fichiers.entreprise.local
dig @192.168.10.10 print-rh.entreprise.local
dig @192.168.10.10 www.entreprise.local
```

**Tous devraient retourner la bonne IP.**

---

**Tester la résolution inverse (PTR) :**

```bash
dig @192.168.10.10 -x 192.168.10.10
```

**Résultat attendu :**

```
;; QUESTION SECTION:
;10.10.168.192.in-addr.arpa.    IN    PTR

;; ANSWER SECTION:
10.10.168.192.in-addr.arpa. 86400 IN    PTR    ns1.entreprise.local.
```

**[OK] Résolution inverse fonctionne !**

---

**Tester avec nslookup (alternative) :**

```bash
nslookup srv-fichiers.entreprise.local 192.168.10.10
```

**Résultat :**

```
Server:        192.168.10.10
Address:    192.168.10.10#53

Name:    srv-fichiers.entreprise.local
Address: 192.168.10.5
```

**[OK] Fonctionne aussi !**

---

Je continue avec la configuration DHCP + DDNS (ÉTAPE 7) ?

### ÉTAPE 7 : Configurer DHCP pour le DNS dynamique

**Maintenant que le DNS fonctionne, configurons DHCP pour qu'il mette à jour automatiquement les enregistrements DNS.**

---

**Éditer la configuration DHCP :**

```bash
sudo nano /etc/dhcp/dhcpd.conf
```

**Modifier/ajouter AU DÉBUT du fichier (avant les déclarations de host et subnet) :**

```conf
# ═══════════════════════════════════════════════════════════════
# CONFIGURATION DNS DYNAMIQUE (DDNS)
# ═══════════════════════════════════════════════════════════════

# ───────────────────────────────────────────────────────────────
# INCLUSION DE LA CLÉ TSIG
# ───────────────────────────────────────────────────────────────

# Inclure la clé TSIG pour sécuriser les mises à jour DNS
include "/etc/bind/ddns.key";

# Explication :
# include = Inclut le contenu d'un autre fichier
#
# /etc/bind/ddns.key contient :
# key "ddns-key" {
#     algorithm hmac-sha256;
#     secret "vJZ3fK8mN2pQ5rT7wX9yA1bC3dE4fG6hI8jK0lM2nO4=";
# };
#
# Cette clé sera utilisée pour signer les requêtes DNS UPDATE
# envoyées au serveur DNS

# ───────────────────────────────────────────────────────────────
# STYLE DE MISE À JOUR DNS
# ───────────────────────────────────────────────────────────────

# Style de mise à jour DNS dynamique
ddns-update-style standard;

# Explication :
# ddns-update-style = Méthode de mise à jour DNS
#
# Valeurs possibles :
#
# standard :
# - Style moderne et recommandé
# - Compatible RFC 2136 (DNS UPDATE)
# - Supporte TSIG
# - Mise à jour forward (A) ET reverse (PTR)
# - * À TOUJOURS utiliser
#
# interim :
# - Ancien style (avant ISC DHCP 3.0)
# - Obsolète et déprécié
# - Ne PAS utiliser
#
# ad-hoc :
# - Style personnalisé
# - Pour des besoins très spécifiques
# - Rarement utilisé
#
# none :
# - Désactive le DDNS
# - Utilisé dans l'exercice 1 et 2
# - Pas de mise à jour DNS automatique

# ───────────────────────────────────────────────────────────────
# COMPORTEMENT DES MISES À JOUR
# ───────────────────────────────────────────────────────────────

# Ignorer les mises à jour DNS du client
ignore client-updates;

# Explication :
# ignore client-updates; = Ignore les demandes de mise à jour DNS du client
#
# Processus DHCP complet :
# 1. Client envoie DHCPDISCOVER
# 2. Client peut inclure l'option "Client FQDN" (option 81)
#    Exemple : "Je veux que mon nom soit pc-alice.entreprise.local"
# 3. Le serveur DHCP peut :
#    - Honorer la demande (allow client-updates;)
#    - Ignorer la demande (ignore client-updates;)
#
# Pourquoi ignorer ?
# - Sécurité : Le client ne choisit pas son propre nom
# - Cohérence : Le serveur DHCP gère TOUS les noms DNS
# - Évite les conflits : Deux clients ne peuvent pas demander le même nom
#
# En entreprise : TOUJOURS ignore client-updates;
#
# Si allow client-updates; :
# - Le client peut choisir son nom DNS
# - Risque : Un client malveillant peut usurper une identité
# - Exemple : Un PC demande "srv-web.entreprise.local"
#   -> Il pourrait intercepter le trafic destiné au vrai serveur web !

# ───────────────────────────────────────────────────────────────
# MISE À JOUR DES DEUX ZONES (FORWARD ET REVERSE)
# ───────────────────────────────────────────────────────────────

# Toujours mettre à jour les deux zones (A et PTR)
update-static-leases on;

# Explication :
# update-static-leases = Mettre à jour le DNS pour les réservations statiques
#
# on :
# - Les réservations DHCP (host declarations) sont AUSSI ajoutées au DNS
# - Exemple : Serveur fichiers (réservation) -> DNS mis à jour
#
# off (défaut) :
# - Seulement les baux dynamiques sont mis à jour dans le DNS
# - Les réservations ne sont PAS ajoutées au DNS
#
# Pourquoi on ?
# - Cohérence : TOUTES les machines sont dans le DNS
# - Simplicité : Pas besoin de dupliquer les enregistrements
#   (une fois dans dhcpd.conf, ils apparaissent automatiquement dans le DNS)
#
# [ATTENTION] Attention :
# Si on a déjà des enregistrements statiques dans le DNS (comme on a fait),
# il peut y avoir des doublons
#
# Solution :
# - Soit supprimer les enregistrements statiques du DNS
# - Soit garder update-static-leases off
#
# Pour cet exercice : on garde ON pour montrer la fonctionnalité

# ───────────────────────────────────────────────────────────────
# NOM DE DOMAINE POUR DDNS
# ───────────────────────────────────────────────────────────────

# Nom de domaine à utiliser pour le DDNS
ddns-domainname "entreprise.local";

# Explication :
# ddns-domainname = Suffixe DNS ajouté aux noms d'hôtes
#
# Exemple :
# - Hostname du client : "pc-alice"
# - ddns-domainname : "entreprise.local"
# - FQDN créé dans le DNS : "pc-alice.entreprise.local"
#
# Le serveur DHCP construit le FQDN complet :
# FQDN = <hostname> + "." + <ddns-domainname>
#
# D'où vient le hostname ?
# - Option DHCP 12 (hostname) envoyée par le client
# - Ou définie dans la réservation (option host-name)
#
# Si le client ne fournit pas de hostname :
# - Certaines implémentations utilisent "dhcp-<IP>"
# - Exemple : dhcp-192-168-10-105

# Nom de domaine pour la zone reverse
ddns-rev-domainname "in-addr.arpa";

# Explication :
# ddns-rev-domainname = Suffixe pour les zones reverse
#
# "in-addr.arpa" = Standard pour IPv4 reverse
# "ip6.arpa" = Standard pour IPv6 reverse
#
# Le serveur DHCP construit automatiquement la zone reverse :
# - IP : 192.168.10.105
# - Zone reverse : 105.10.168.192.in-addr.arpa
# - Domaine parent : in-addr.arpa

# ───────────────────────────────────────────────────────────────
# ZONES DNS À METTRE À JOUR
# ───────────────────────────────────────────────────────────────

# Zone DNS forward (nom -> IP)
zone entreprise.local. {
    # Serveur DNS primaire à contacter
    primary 192.168.10.10;
    
    # Explication :
    # primary = Adresse IP du serveur DNS primaire (master)
    # 192.168.10.10 = Notre serveur DNS (BIND9)
    #
    # Le serveur DHCP enverra les requêtes DNS UPDATE à cette adresse
    #
    # Si plusieurs serveurs DNS :
    # primary 192.168.10.10;
    # primary 192.168.10.11;
    # Le DHCP essaiera le premier, puis le second en cas d'échec
    
    # Clé TSIG pour authentifier les mises à jour
    key ddns-key;
    
    # Explication :
    # key = Clé TSIG à utiliser pour signer les requêtes
    # ddns-key = Nom de la clé (définie dans /etc/bind/ddns.key)
    #
    # Processus d'authentification :
    # 1. DHCP prépare une requête DNS UPDATE
    #    Exemple : "Ajoute pc-alice.entreprise.local A 192.168.10.105"
    # 2. DHCP signe la requête avec la clé ddns-key (HMAC-SHA256)
    # 3. DHCP envoie la requête signée au DNS (192.168.10.10)
    # 4. DNS vérifie la signature avec SA copie de ddns-key
    # 5. Si signature valide -> DNS applique la mise à jour
    # 6. Si signature invalide -> DNS rejette (erreur REFUSED)
    #
    # Sans la clé :
    # Le DNS rejetterait TOUTES les mises à jour (allow-update { key "ddns-key"; })
}

# Zone DNS reverse (IP -> nom)
zone 10.168.192.in-addr.arpa. {
    # Serveur DNS primaire
    primary 192.168.10.10;
    
    # Clé TSIG
    key ddns-key;
    
    # Explication :
    # Identique à la zone forward
    # Mais pour les enregistrements PTR
    #
    # Quand DHCP attribue 192.168.10.105 à pc-alice :
    # 1. Mise à jour forward :
    #    Ajoute : pc-alice.entreprise.local A 192.168.10.105
    # 2. Mise à jour reverse :
    #    Ajoute : 105.10.168.192.in-addr.arpa PTR pc-alice.entreprise.local
    #
    # Les DEUX mises à jour sont signées avec ddns-key
}

# ═══════════════════════════════════════════════════════════════
```

**Important : Cette configuration s'ajoute AVANT les déclarations existantes (réservations, subnet, pools).**

**Structure finale du fichier :**

```
1. Configuration DDNS (ce qu'on vient d'ajouter)
2. Réservations (host declarations)
3. Subnet et pools
```

---

**Sauvegarde : `Ctrl + O`, `Entrée`, `Ctrl + X`**

---

**Vérifier la syntaxe :**

```bash
sudo dhcpd -t -cf /etc/dhcp/dhcpd.conf
```

**Si syntaxe correcte, pas de message d'erreur.**

---

**Redémarrer le service DHCP :**

```bash
sudo systemctl restart isc-dhcp-server
```

**Vérifier l'état :**

```bash
sudo systemctl status isc-dhcp-server
```

**Résultat attendu : `Active: active (running)`**

---

### ÉTAPE 8 : Tester le DNS dynamique

**Maintenant, testons que DHCP met automatiquement à jour le DNS !**

---

#### Test 1 : Client obtient une IP dynamique

**Sur une machine cliente (ou VM) :**

**1. Définir le hostname :**

```bash
sudo hostnamectl set-hostname pc-alice
```

**Explication :**
- `hostnamectl` = Utilitaire systemd pour gérer le hostname
- Le hostname sera envoyé au serveur DHCP (option 12)

---

**2. Libérer l'adresse IP actuelle (si présente) :**

```bash
sudo dhclient -r ens33
```

---

**3. Demander une nouvelle adresse IP :**

```bash
sudo dhclient -v ens33
```

**Résultat attendu :**

```
Internet Systems Consortium DHCP Client 4.4.1
Listening on LPF/ens33/00:0c:29:ab:cd:ef
Sending on   LPF/ens33/00:0c:29:ab:cd:ef
Sending on   Socket/fallback
DHCPDISCOVER on ens33 to 255.255.255.255 port 67 interval 3
DHCPOFFER of 192.168.10.105 from 192.168.10.10
DHCPREQUEST for 192.168.10.105 on ens33 to 255.255.255.255 port 67
DHCPACK of 192.168.10.105 from 192.168.10.10
bound to 192.168.10.105 -- renewal in 43200 seconds.
```

**[OK] Client a obtenu l'IP 192.168.10.105**

---

**4. Sur le SERVEUR DHCP, vérifier les logs :**

```bash
sudo tail -30 /var/log/syslog | grep dhcpd
```

**Résultat attendu :**

```
Dec 16 23:00:00 dhcp-server dhcpd[12345]: DHCPDISCOVER from 00:0c:29:ab:cd:ef via ens33
Dec 16 23:00:00 dhcp-server dhcpd[12345]: DHCPOFFER on 192.168.10.105 to 00:0c:29:ab:cd:ef via ens33
Dec 16 23:00:01 dhcp-server dhcpd[12345]: DHCPREQUEST for 192.168.10.105 from 00:0c:29:ab:cd:ef via ens33
Dec 16 23:00:01 dhcp-server dhcpd[12345]: DHCPACK on 192.168.10.105 to 00:0c:29:ab:cd:ef via ens33
Dec 16 23:00:01 dhcp-server dhcpd[12345]: Added new forward map from pc-alice.entreprise.local to 192.168.10.105
Dec 16 23:00:01 dhcp-server dhcpd[12345]: Added reverse map from 105.10.168.192.in-addr.arpa. to pc-alice.entreprise.local
```

**Points importants :**

**`Added new forward map from pc-alice.entreprise.local to 192.168.10.105`**
- [OK] Enregistrement A ajouté au DNS !
- pc-alice.entreprise.local -> 192.168.10.105

**`Added reverse map from 105.10.168.192.in-addr.arpa. to pc-alice.entreprise.local`**
- [OK] Enregistrement PTR ajouté au DNS !
- 192.168.10.105 -> pc-alice.entreprise.local

**Si tu NE vois PAS ces lignes, il y a un problème (voir erreurs courantes plus bas).**

---

**5. Vérifier que le DNS a bien été mis à jour :**

**Résolution directe (nom -> IP) :**

```bash
dig @192.168.10.10 pc-alice.entreprise.local
```

**Résultat attendu :**

```
;; QUESTION SECTION:
;pc-alice.entreprise.local.    IN    A

;; ANSWER SECTION:
pc-alice.entreprise.local. 86400 IN    A    192.168.10.105

;; AUTHORITY SECTION:
entreprise.local.    86400    IN    NS    ns1.entreprise.local.
```

**[OK] Enregistrement A créé automatiquement par DDNS !**

---

**Résolution inverse (IP -> nom) :**

```bash
dig @192.168.10.10 -x 192.168.10.105
```

**Résultat attendu :**

```
;; QUESTION SECTION:
;105.10.168.192.in-addr.arpa.    IN    PTR

;; ANSWER SECTION:
105.10.168.192.in-addr.arpa. 86400 IN PTR pc-alice.entreprise.local.
```

**[OK] Enregistrement PTR créé automatiquement par DDNS !**

---

**6. Tester avec ping (depuis n'importe quelle machine) :**

```bash
ping -c 3 pc-alice.entreprise.local
```

**Résultat attendu :**

```
PING pc-alice.entreprise.local (192.168.10.105) 56(84) bytes of data.
64 bytes from 192.168.10.105: icmp_seq=1 ttl=64 time=0.234 ms
64 bytes from 192.168.10.105: icmp_seq=2 ttl=64 time=0.187 ms
64 bytes from 192.168.10.105: icmp_seq=3 ttl=64 time=0.201 ms

--- pc-alice.entreprise.local ping statistics ---
3 packets transmitted, 3 received, 0% packet loss
```

**[OK] Résolution DNS fonctionne !**

---

**7. Vérifier le fichier de zone DNS :**

```bash
sudo cat /var/lib/bind/db.entreprise.local
```

**À la fin du fichier, tu devrais voir :**

```
; DDNS updates (added automatically)
pc-alice.entreprise.local. 86400 IN A 192.168.10.105
```

**[OK] Enregistrement ajouté automatiquement par BIND !**

---

**Fichier de zone reverse :**

```bash
sudo cat /var/lib/bind/db.192.168.10
```

**À la fin :**

```
; DDNS updates
105.10.168.192.in-addr.arpa. 86400 IN PTR pc-alice.entreprise.local.
```

**[OK] Enregistrement PTR ajouté !**

---

#### Test 2 : Libération du bail et suppression DNS

**Maintenant, testons que quand le client libère son IP, le DNS est mis à jour.**

**Sur le client (pc-alice) :**

```bash
sudo dhclient -r ens33
```

**Option `-r` = Release (libérer le bail)**

---

**Sur le SERVEUR DHCP, vérifier les logs :**

```bash
sudo tail -10 /var/log/syslog | grep dhcpd
```

**Résultat attendu :**

```
Dec 16 23:10:00 dhcp-server dhcpd[12345]: DHCPRELEASE of 192.168.10.105 from 00:0c:29:ab:cd:ef via ens33 (found)
Dec 16 23:10:00 dhcp-server dhcpd[12345]: Removed forward map from pc-alice.entreprise.local to 192.168.10.105
Dec 16 23:10:00 dhcp-server dhcpd[12345]: Removed reverse map on 105.10.168.192.in-addr.arpa.
```

**Points importants :**

**`DHCPRELEASE of 192.168.10.105`**
- Le client a libéré son IP

**`Removed forward map`**
- [OK] Enregistrement A supprimé du DNS

**`Removed reverse map`**
- [OK] Enregistrement PTR supprimé du DNS

---

**Vérifier que l'enregistrement DNS a été supprimé :**

```bash
dig @192.168.10.10 pc-alice.entreprise.local
```

**Résultat attendu :**

```
;; QUESTION SECTION:
;pc-alice.entreprise.local.    IN    A

;; AUTHORITY SECTION:
entreprise.local.    86400    IN    SOA    ns1.entreprise.local. admin.entreprise.local. ...

;; Query time: 1 msec
;; SERVER: 192.168.10.10#53
;; WHEN: Mon Dec 16 23:11:00 UTC 2024
;; MSG SIZE  rcvd: 98
```

**Pas de section ANSWER !**

**[OK] L'enregistrement a bien été supprimé !**

---

#### Test 3 : Plusieurs clients simultanés

**Pour tester la scalabilité, connectons plusieurs clients :**

**Client 1 : pc-alice -> 192.168.10.105**
**Client 2 : pc-bob -> 192.168.10.106**
**Client 3 : pc-charlie -> 192.168.10.107**

**Sur chaque client :**

```bash
sudo hostnamectl set-hostname pc-bob  # ou pc-charlie
sudo dhclient -v ens33
```

---

**Vérifier que tous sont dans le DNS :**

```bash
dig @192.168.10.10 pc-alice.entreprise.local +short
dig @192.168.10.10 pc-bob.entreprise.local +short
dig @192.168.10.10 pc-charlie.entreprise.local +short
```

**Résultat :**

```
192.168.10.105
192.168.10.106
192.168.10.107
```

**[OK] Tous les clients sont dans le DNS !**

---

**Option `+short` = Affiche seulement la réponse (pas les détails)**

---

**Lister TOUS les enregistrements de la zone :**

```bash
dig @192.168.10.10 entreprise.local AXFR
```

**AXFR = Zone Transfer (transfert de zone complet)**

**Résultat (extrait) :**

```
entreprise.local.    86400    IN    SOA    ns1.entreprise.local. ...
entreprise.local.    86400    IN    NS     ns1.entreprise.local.
ns1.entreprise.local.    86400    IN    A    192.168.10.10
srv-fichiers.entreprise.local. 86400 IN A 192.168.10.5
print-rh.entreprise.local. 86400 IN A 192.168.10.6
srv-web.entreprise.local. 86400 IN A 192.168.10.7
pc-alice.entreprise.local. 86400 IN A 192.168.10.105
pc-bob.entreprise.local. 86400 IN A 192.168.10.106
pc-charlie.entreprise.local. 86400 IN A 192.168.10.107
```

**[OK] Tous les enregistrements visibles !**

---

### ÉTAPE 9 : Configurer les clients pour utiliser le DNS local

**Pour que les clients puissent résoudre les noms sans spécifier le serveur DNS à chaque fois, il faut qu'ils utilisent notre DNS (192.168.10.10) par défaut.**

**C'est déjà configuré ! [BRAVO]**

**Dans l'exercice 2, on a défini dans `dhcpd.conf` :**

```conf
option domain-name-servers 192.168.10.10, 8.8.8.8;
```

**Les clients DHCP reçoivent automatiquement :**
- DNS primaire : 192.168.10.10 (notre DNS local)
- DNS secondaire : 8.8.8.8 (Google DNS en backup)

---

**Vérifier sur un client :**

```bash
cat /etc/resolv.conf
```

**Résultat :**

```
# Generated by NetworkManager
nameserver 192.168.10.10
nameserver 8.8.8.8
search entreprise.local
```

**Points importants :**

**`nameserver 192.168.10.10`**
- [OK] DNS local en premier

**`search entreprise.local`**
- [OK] Suffixe de recherche
- Permet de taper `ping pc-alice` au lieu de `ping pc-alice.entreprise.local`

---

**Tester la résolution sans spécifier le serveur :**

```bash
ping pc-alice
```

**Grâce à `search entreprise.local`, le système essaie automatiquement `pc-alice.entreprise.local`**

**Résultat :**

```
PING pc-alice.entreprise.local (192.168.10.105) 56(84) bytes of data.
64 bytes from pc-alice.entreprise.local (192.168.10.105): icmp_seq=1 ttl=64 time=0.234 ms
```

**[OK] Fonctionne sans le FQDN complet !**

---

**Tester la résolution d'un domaine externe (forwarding) :**

```bash
ping google.com
```

**Processus :**
1. Client demande à 192.168.10.10 : "Quelle est l'IP de google.com ?"
2. BIND9 ne gère pas google.com (pas dans nos zones)
3. BIND9 forward la requête à 8.8.8.8 (forwarders)
4. 8.8.8.8 répond avec l'IP de google.com
5. BIND9 retourne l'IP au client
6. Client ping google.com

**Résultat :**

```
PING google.com (142.250.185.46) 56(84) bytes of data.
64 bytes from par21s17-in-f14.1e100.net (142.250.185.46): icmp_seq=1 ttl=117 time=15.2 ms
```

**[OK] Résolution externe fonctionne aussi !**

---

### ÉTAPE 10 : Monitoring et maintenance DDNS

**Surveiller les mises à jour DNS :**

```bash
# Logs DHCP (mises à jour)
sudo tail -f /var/log/syslog | grep "Added\|Removed"
```

**Affiche en temps réel les ajouts/suppressions DNS.**

---

**Logs BIND (réception des mises à jour) :**

```bash
# Logs BIND
sudo tail -f /var/log/syslog | grep named
```

**Ou activer des logs plus détaillés :**

```bash
sudo nano /etc/bind/named.conf.options
```

**Ajouter dans le bloc `options` :**

```conf
// Logs détaillés pour DDNS
logging {
    channel ddns_log {
        file "/var/log/bind/ddns.log" versions 3 size 5m;
        severity info;
        print-category yes;
        print-severity yes;
        print-time yes;
    };
    category update { ddns_log; };
    category update-security { ddns_log; };
};
```

**Créer le dossier de logs :**

```bash
sudo mkdir -p /var/log/bind
sudo chown bind:bind /var/log/bind
```

**Redémarrer BIND :**

```bash
sudo systemctl restart bind9
```

**Consulter les logs DDNS :**

```bash
sudo tail -f /var/log/bind/ddns.log
```

**Résultat (exemple) :**

```
16-Dec-2024 23:00:01.234 update: info: client @0x7f1234567890 192.168.10.10#52341/key ddns-key: updating zone 'entreprise.local/IN': adding an RR at 'pc-alice.entreprise.local' A 192.168.10.105
16-Dec-2024 23:00:01.235 update: info: client @0x7f1234567890 192.168.10.10#52341/key ddns-key: updating zone '10.168.192.in-addr.arpa/IN': adding an RR at '105.10.168.192.in-addr.arpa' PTR pc-alice.entreprise.local.
```

**Détails visibles :**
- Timestamp précis
- Client : 192.168.10.10 (serveur DHCP)
- Authentification : key ddns-key [OK]
- Action : adding an RR (Resource Record)
- Zone : entreprise.local
- Enregistrement : pc-alice A 192.168.10.105

---

**Statistiques des zones DNS :**

```bash
# Nombre d'enregistrements dans la zone forward
sudo grep -c "IN A" /var/lib/bind/db.entreprise.local

# Nombre d'enregistrements dans la zone reverse
sudo grep -c "IN PTR" /var/lib/bind/db.192.168.10
```

---

**Sauvegardes automatiques des zones :**

**BIND crée automatiquement des fichiers journaux :**

```bash
ls -lh /var/lib/bind/
```

**Résultat :**

```
-rw-r--r-- 1 bind bind 1.2K Dec 16 23:00 db.entreprise.local
-rw-r--r-- 1 bind bind  512 Dec 16 23:00 db.entreprise.local.jnl
-rw-r--r-- 1 bind bind  987 Dec 16 23:00 db.192.168.10
-rw-r--r-- 1 bind bind  256 Dec 16 23:00 db.192.168.10.jnl
```

**Fichiers `.jnl` = Journal des modifications**
- Contient l'historique des mises à jour DDNS
- Appliqué au redémarrage de BIND
- Format binaire (pas lisible directement)

**Quand BIND redémarre :**
1. Charge le fichier de zone principal (db.entreprise.local)
2. Applique les modifications du journal (.jnl)
3. Résultat : Zone à jour avec toutes les modifications DDNS

---

**Forcer l'écriture du journal dans le fichier de zone :**

```bash
sudo rndc sync
```

**`rndc` = Remote Name Daemon Control**

**Résultat :**
- Le journal (.jnl) est fusionné dans le fichier de zone principal
- Le fichier de zone est réécrit avec TOUS les enregistrements
- Le journal est vidé

**Utile avant une sauvegarde.**

---

### [OK] TESTS DE VALIDATION

**1. DDNS fonctionne (ajout) :**

- [ ] Client obtient une IP via DHCP
- [ ] Logs DHCP montrent "Added new forward map"
- [ ] Logs DHCP montrent "Added reverse map"
- [ ] `dig pc-<nom>.entreprise.local` retourne l'IP correcte
- [ ] `dig -x <IP>` retourne le nom correct
- [ ] `ping pc-<nom>.entreprise.local` fonctionne

---

**2. DDNS fonctionne (suppression) :**

- [ ] Client libère son IP (`dhclient -r`)
- [ ] Logs DHCP montrent "Removed forward map"
- [ ] Logs DHCP montrent "Removed reverse map"
- [ ] `dig pc-<nom>.entreprise.local` ne retourne plus de résultat

---

**3. Sécurité TSIG :**

**Tester une mise à jour DNS sans TSIG (devrait échouer) :**

```bash
nsupdate <<EOF
server 192.168.10.10
update add test.entreprise.local 86400 A 1.2.3.4
send
EOF
```

**Résultat attendu :**

```
update failed: REFUSED
```

**[OK] La mise à jour est refusée sans TSIG !**

---

**Tester avec TSIG (devrait réussir) :**

```bash
nsupdate -k /etc/bind/ddns.key <<EOF
server 192.168.10.10
update add test.entreprise.local 86400 A 1.2.3.4
send
EOF
```

**Résultat attendu :**
- Pas d'erreur

**Vérifier :**

```bash
dig @192.168.10.10 test.entreprise.local +short
```

**Résultat : `1.2.3.4`**

**[OK] Mise à jour réussie avec TSIG !**

---

**Nettoyer :**

```bash
nsupdate -k /etc/bind/ddns.key <<EOF
server 192.168.10.10
update delete test.entreprise.local A
send
EOF
```

---

**4. Résolution depuis les clients :**

- [ ] Client configuré avec DNS = 192.168.10.10 (via DHCP)
- [ ] `cat /etc/resolv.conf` montre `nameserver 192.168.10.10`
- [ ] `cat /etc/resolv.conf` montre `search entreprise.local`
- [ ] `ping pc-alice` fonctionne (sans le domaine complet)
- [ ] `ping google.com` fonctionne (forwarding)

---

**5. Cohérence forward <-> reverse :**

**Pour chaque IP attribuée, vérifier :**

```bash
# Résolution directe
IP=$(dig @192.168.10.10 pc-alice.entreprise.local +short)

# Résolution inverse
NAME=$(dig @192.168.10.10 -x $IP +short)

echo "Forward: pc-alice.entreprise.local -> $IP"
echo "Reverse: $IP -> $NAME"
```

**Les deux DOIVENT correspondre !**

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "Unable to add forward map" dans les logs DHCP

**Message complet :**

```
dhcpd[12345]: Unable to add forward map from pc-alice.entreprise.local to 192.168.10.105: timed out
```

**Causes possibles :**

**1. Le serveur DNS ne répond pas**

**Vérifier que BIND tourne :**

```bash
sudo systemctl status bind9
```

**Si inactif, démarrer :**

```bash
sudo systemctl start bind9
```

---

**2. BIND n'écoute pas sur la bonne interface**

**Vérifier :**

```bash
sudo netstat -tulpn | grep :53
```

**Si BIND écoute seulement sur 127.0.0.1 :**

```
tcp    0    0 127.0.0.1:53    0.0.0.0:*    LISTEN    23456/named
```

**Modifier `/etc/bind/named.conf.options` :**

```conf
listen-on { any; };  // Au lieu de { 127.0.0.1; }
```

**Redémarrer BIND :**

```bash
sudo systemctl restart bind9
```

---

**3. Pare-feu bloque le port 53**

```bash
sudo ufw status
```

**Si actif, autoriser :**

```bash
sudo ufw allow 53/tcp
sudo ufw allow 53/udp
```

---

**4. Adresse du serveur DNS incorrecte dans dhcpd.conf**

**Vérifier dans `/etc/dhcp/dhcpd.conf` :**

```conf
zone entreprise.local. {
    primary 192.168.10.10;  // Doit être l'IP CORRECTE du serveur DNS
}
```

---

#### Erreur 2 : "REFUSED" dans les logs BIND

**Message complet :**

```
named[23456]: client @0x... 192.168.10.10#52341/key ddns-key: updating zone 'entreprise.local/IN': update failed: REFUSED
```

**Causes possibles :**

**1. Clé TSIG incorrecte ou manquante**

**Vérifier que la clé est identique dans les deux fichiers :**

```bash
# DHCP
sudo grep secret /etc/bind/ddns.key

# BIND
sudo grep secret /etc/bind/ddns.key
```

**Doivent être EXACTEMENT identiques !**

**Si différentes, regénérer et remplacer partout :**

```bash
sudo tsig-keygen -a HMAC-SHA256 ddns-key > /tmp/new-ddns.key
sudo cp /tmp/new-ddns.key /etc/bind/ddns.key
sudo systemctl restart bind9
sudo systemctl restart isc-dhcp-server
```

---

**2. Zone DNS n'autorise pas les mises à jour**

**Vérifier dans `/etc/bind/named.conf.local` :**

```conf
zone "entreprise.local" {
    type master;
    file "/var/lib/bind/db.entreprise.local";
    allow-update { key "ddns-key"; };  // <- Doit être présent !
};
```

**Si `allow-update { none; };` ou absent, les mises à jour sont refusées.**

---

**3. Nom de clé incorrect**

**Dans `dhcpd.conf` :**

```conf
key ddns-key;  // Nom de la clé
```

**Dans `named.conf.local` :**

```conf
allow-update { key "ddns-key"; };  // Même nom !
```

**Les noms DOIVENT correspondre.**

---

#### Erreur 3 : Enregistrements DNS ne sont pas supprimés

**Symptôme :**

Le client libère son IP, mais l'enregistrement DNS reste.

**Causes possibles :**

**1. Le client n'envoie pas de DHCPRELEASE**

**Certains clients ne libèrent pas proprement leur bail.**

**Solution :**

- Attendre l'expiration naturelle du bail
- Ou supprimer manuellement avec `nsupdate`

**Exemple :**

```bash
nsupdate -k /etc/bind/ddns.key <<EOF
server 192.168.10.10
update delete pc-alice.entreprise.local A
send
EOF
```

---

**2. Option `update-static-leases` mal configurée**

**Si `update-static-leases off;` dans `dhcpd.conf` :**

Les réservations statiques ne sont pas supprimées du DNS.

---

#### Erreur 4 : Résolution inverse ne fonctionne pas

**Symptôme :**

`dig pc-alice.entreprise.local` fonctionne, mais `dig -x 192.168.10.105` échoue.

**Causes possibles :**

**1. Zone reverse mal configurée**

**Vérifier le nom de zone dans `named.conf.local` :**

```conf
zone "10.168.192.in-addr.arpa" {  // Pour 192.168.10.0/24
    ...
}
```

**Nom de zone DOIT correspondre au réseau (inversé).**

---

**2. Zone reverse non référencée dans dhcpd.conf**

**Vérifier dans `/etc/dhcp/dhcpd.conf` :**

```conf
zone 10.168.192.in-addr.arpa. {
    primary 192.168.10.10;
    key ddns-key;
}
```

**Si absent, DHCP ne mettra pas à jour la zone reverse.**

---

#### Erreur 5 : "journal file ... out of sync with zone"

**Message complet :**

```
named[23456]: dumping master file: /var/lib/bind/db.entreprise.local.jnl: journal file out of sync with zone
```

**Cause :**

Le fichier journal (.jnl) est corrompu ou désynchronisé.

**Solution :**

**1. Arrêter BIND :**

```bash
sudo systemctl stop bind9
```

**2. Supprimer les fichiers journaux :**

```bash
sudo rm /var/lib/bind/*.jnl
```

**3. Redémarrer BIND :**

```bash
sudo systemctl start bind9
```

**BIND recréera des journaux propres.**

**[ATTENTION] Les mises à jour DDNS en cours sont perdues !**

**Pour éviter cela, faire régulièrement :**

```bash
sudo rndc sync
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. DNS dynamique (DDNS)**
- Mise à jour automatique des enregistrements DNS
- Synchronisation DHCP <-> DNS
- Forward (A) ET Reverse (PTR) mis à jour ensemble

**2. Processus DDNS**
- Client obtient IP via DHCP
- DHCP contacte DNS avec DNS UPDATE
- DNS valide TSIG et applique la mise à jour
- Enregistrements ajoutés dans les zones

**3. Sécurisation TSIG**
- Transaction SIGnature (authentification)
- Clé partagée secrète (HMAC-SHA256)
- Empêche les mises à jour non autorisées
- TOUJOURS utiliser en production

**4. Zones DNS**
- Forward : Nom -> IP (enregistrement A)
- Reverse : IP -> Nom (enregistrement PTR)
- Les deux DOIVENT être cohérentes

**5. Configuration DHCP pour DDNS**
- `ddns-update-style standard;`
- `ignore client-updates;` (sécurité)
- `zone <nom> { primary <IP>; key <clé>; }`

**6. Configuration BIND pour DDNS**
- `allow-update { key "ddns-key"; };`
- Zones dans `/var/lib/bind/` (lecture/écriture)
- Fichiers journaux (.jnl) pour historique

**7. Bonnes pratiques**
- Utiliser TSIG TOUJOURS
- Logs séparés pour DDNS
- `rndc sync` régulièrement
- Surveiller les mises à jour
- Tester forward ET reverse

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. DNSSEC avec DDNS**

**DNSSEC** = DNS Security Extensions (signature cryptographique des zones)

**Activer DNSSEC pour les zones dynamiques :**

```bash
# Générer les clés DNSSEC
sudo dnssec-keygen -a RSASHA256 -b 2048 -n ZONE entreprise.local
sudo dnssec-keygen -a RSASHA256 -b 2048 -f KSK -n ZONE entreprise.local

# Configurer la zone pour signer automatiquement
# Dans named.conf.local :
zone "entreprise.local" {
    type master;
    file "/var/lib/bind/db.entreprise.local";
    allow-update { key "ddns-key"; };
    auto-dnssec maintain;
    inline-signing yes;
};
```

---

**2. Plusieurs serveurs DNS (haute disponibilité)**

**Configuration master-slave :**

**Sur le serveur primaire (master) :**

```conf
zone "entreprise.local" {
    type master;
    file "/var/lib/bind/db.entreprise.local";
    allow-update { key "ddns-key"; };
    
    # Autoriser les transferts de zone vers le slave
    allow-transfer { 192.168.10.11; };
    
    # Notifier le slave des changements
    notify yes;
    also-notify { 192.168.10.11; };
};
```

**Sur le serveur secondaire (slave - 192.168.10.11) :**

```conf
zone "entreprise.local" {
    type slave;
    file "/var/cache/bind/db.entreprise.local";
    masters { 192.168.10.10; };
    
    # Le slave ne peut pas recevoir de mises à jour DDNS
    # Seulement des transferts de zone depuis le master
};
```

**Dans `dhcpd.conf`, spécifier les deux serveurs :**

```conf
zone entreprise.local. {
    primary 192.168.10.10;  # Master
    primary 192.168.10.11;  # Slave (fallback)
    key ddns-key;
}
```

---

**3. Scavenging (nettoyage automatique)**

**Problème :**

Les enregistrements DNS peuvent s'accumuler si les clients ne libèrent pas proprement leur bail.

**Solution (Windows DNS Server) :**

Windows DNS Server supporte le "scavenging" natif.

**Solution (BIND) :**

Script cron pour nettoyer les enregistrements obsolètes :

```bash
#!/bin/bash
# Nettoie les enregistrements DNS dont le bail DHCP a expiré

LEASES_FILE="/var/lib/dhcp/dhcpd.leases"
ZONE="entreprise.local"
KEYFILE="/etc/bind/ddns.key"

# Extraire les baux actifs
ACTIVE_HOSTNAMES=$(grep "binding state active" -B 5 "$LEASES_FILE" | grep "client-hostname" | awk '{print $2}' | tr -d '";')

# Lister tous les enregistrements A de la zone
ALL_RECORDS=$(dig @localhost "$ZONE" AXFR | grep "IN A" | awk '{print $1}' | sed 's/\.$//')

# Comparer et supprimer les obsolètes
for RECORD in $ALL_RECORDS; do
    if ! echo "$ACTIVE_HOSTNAMES" | grep -q "^$RECORD$"; then
        echo "Suppression de $RECORD (bail expiré)"
        nsupdate -k "$KEYFILE" <<EOF
server localhost
update delete ${RECORD}.${ZONE}. A
send
EOF
    fi
done
```

**Programmer dans cron (quotidiennement à 3h) :**

```bash
sudo crontab -e
```

**Ajouter :**

```
0 3 * * * /usr/local/bin/dns-scavenge.sh
```

---

**4. Logging avancé avec ELK Stack**

**ELK** = Elasticsearch + Logstash + Kibana

**Centraliser et visualiser les logs DHCP + DNS :**

1. **Logstash** collecte les logs de `/var/log/syslog`
2. **Elasticsearch** indexe et stocke les logs
3. **Kibana** visualise les données

**Dashboard Kibana possible :**
- Nombre d'attributions DHCP par heure
- Top 10 des clients les plus actifs
- Erreurs DDNS (REFUSED, timeout)
- Carte de chaleur des mises à jour DNS

---

**5. IPv6 avec DHCPv6 et DNS**

**Configuration similaire pour IPv6 :**

```conf
# dhcpd6.conf
ddns-update-style standard;
ddns-domainname "entreprise.local";

zone entreprise.local. {
    primary6 2001:db8::10;
    key ddns-key;
}

zone 0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. {
    primary6 2001:db8::10;
    key ddns-key;
}

subnet6 2001:db8::/64 {
    range6 2001:db8::100 2001:db8::200;
    option dhcp6.name-servers 2001:db8::10;
}
```

**Zone reverse IPv6 : ip6.arpa (au lieu de in-addr.arpa)**

---

**6. Intégration avec Active Directory (AD)**

**Pour les environnements mixtes Windows/Linux :**

**Windows AD peut servir de serveur DNS :**
- Supporte DDNS nativement
- Intégration avec Kerberos (authentification)
- Scavenging automatique

**ISC DHCP peut mettre à jour un DNS Windows AD :**

```conf
# Utiliser GSS-TSIG (Kerberos) au lieu de TSIG
ddns-update-style standard;

zone entreprise.local. {
    primary 192.168.10.5;  # Contrôleur de domaine AD
    # Pas de clé TSIG, utilise Kerberos
}
```

**Configuration Kerberos nécessaire (complexe).**

---

## [COURS] CONCLUSION DE L'EXERCICE 3

**[OK] Félicitations ! Tu maîtrises maintenant l'intégration DHCP + DNS dynamique !**

**Ce que tu as appris :**
- Installer et configurer BIND9 (serveur DNS)
- Créer des zones DNS forward et reverse
- Comprendre les enregistrements DNS (A, PTR, SOA, NS, CNAME, MX)
- Générer une clé TSIG pour sécuriser les mises à jour
- Configurer le DDNS dans ISC DHCP Server
- Intégrer DHCP et DNS avec mise à jour automatique
- Tester la résolution DNS directe et inverse
- Monitorer les mises à jour DDNS
- Déboguer les problèmes DDNS

**Compétences acquises :**
- [OK] Configuration DNS (niveau intermédiaire)
- [OK] Zones DNS forward et reverse
- [OK] DDNS (DNS dynamique)
- [OK] Sécurisation TSIG
- [OK] Intégration DHCP <-> DNS
- [OK] Troubleshooting DNS

**Temps moyen de réalisation :** 2-3 heures

**Prochaine étape :** Exercice 4 - Haute disponibilité DHCP (Failover) ! [SYNC]

---

Je continue avec l'exercice 4 ?

# [ROUGE] EXERCICE 4 : HAUTE DISPONIBILITÉ DHCP (FAILOVER)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu continues ton travail d'administrateur réseau dans la PME. L'infrastructure DHCP + DNS fonctionne parfaitement, mais le responsable IT soulève une préoccupation critique :

**"Que se passe-t-il si le serveur DHCP tombe en panne ?"**

**Impact d'une panne DHCP :**
- Les nouveaux employés ne peuvent pas se connecter au réseau
- Les machines dont le bail expire ne peuvent pas le renouveler
- Les téléphones VOIP perdent leur configuration réseau
- Interruption progressive de l'activité (à mesure que les baux expirent)

**Mission :** Mettre en place une architecture DHCP haute disponibilité (HA) avec :
1. **Deux serveurs DHCP** fonctionnant en tandem
2. **Partage d'état** (synchronisation des baux entre serveurs)
3. **Basculement automatique** en cas de panne d'un serveur
4. **Répartition de charge** (load balancing) pour optimiser les performances

### Cahier des charges

**Architecture cible :**
```
┌─────────────────────────────────────────────────────────────┐
│                     Réseau 192.168.10.0/24                  │
└─────────────────────────────────────────────────────────────┘
                              │
        ┌─────────────────────┼─────────────────────┐
        │                     │                     │
┌───────[BLACK_DOWN-POINTING_TRIANGLE]────────┐    ┌───────[BLACK_DOWN-POINTING_TRIANGLE]────────┐    ┌──────[BLACK_DOWN-POINTING_TRIANGLE]──────┐
│ DHCP Primary   │    │ DHCP Secondary │    │   Clients   │
│ 192.168.10.10  │[BLACK_LEFT-POINTING_POINTER]──[BLACK_RIGHT-POINTING_POINTER]│ 192.168.10.11  │    │             │
│ (actif)        │    │ (actif)        │    │             │
└────────────────┘    └────────────────┘    └─────────────┘
        [BLACK_UP-POINTING_TRIANGLE]                     [BLACK_UP-POINTING_TRIANGLE]
        │  Synchronisation    │
        │  État (port 847)    │
        └─────────────────────┘

Fonctionnement normal :
├─ Primary gère 50% des requêtes
├─ Secondary gère 50% des requêtes
└─ Synchronisation continue de l'état

Panne du Primary :
├─ Secondary prend en charge 100% des requêtes
└─ Utilise l'état synchronisé pour continuer le service

Panne du Secondary :
├─ Primary prend en charge 100% des requêtes
└─ Continue normalement
```

**Spécifications techniques :**

**Mode Failover : load balance**
- Les deux serveurs répondent aux requêtes (répartition 50/50)
- Synchronisation d'état en temps réel via port TCP 847
- Basculement automatique si un serveur tombe

**Partage des adresses :**
- Plage globale : 192.168.10.100-200 (101 adresses)
- Primary responsable : 192.168.10.100-150 (51 adresses)
- Secondary responsable : 192.168.10.151-200 (50 adresses)
- En cas de panne, le survivant utilise TOUTE la plage

**Authentification :**
- Secret partagé pour sécuriser la communication entre serveurs
- Empêche un serveur DHCP malveillant de rejoindre le failover

### Contraintes techniques

- Deux serveurs Ubuntu 22.04 (ou VMs)
- Configuration DHCP + DNS existante (exercices 1-3)
- Synchronisation d'état fiable
- Tests de résilience complets
- Documentation de la procédure de reprise après panne
- Temps estimé : 3-4 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Comprendre les concepts de haute disponibilité (HA)
- [OK] Configurer le failover DHCP avec ISC DHCP Server
- [OK] Mettre en place la communication peer-to-peer entre serveurs
- [OK] Générer et utiliser un secret partagé pour l'authentification
- [OK] Configurer le load balancing DHCP
- [OK] Synchroniser l'état des baux entre serveurs
- [OK] Tester la résilience en cas de panne
- [OK] Monitorer l'état du failover
- [OK] Gérer les transitions d'état (normal, partner-down, etc.)
- [OK] Documenter une architecture haute disponibilité

---

## [DOCS] PRÉREQUIS

- Exercices 1, 2 et 3 terminés avec succès
- Serveur DHCP primaire fonctionnel (192.168.10.10)
- Deuxième serveur disponible (192.168.10.11)
- Compréhension des concepts réseau de base
- Accès administrateur aux deux serveurs

---

## [IDEE] COMPRENDRE LA HAUTE DISPONIBILITÉ DHCP

### Qu'est-ce que la haute disponibilité (HA) ?

**Haute disponibilité** = Garantir qu'un service reste disponible même en cas de panne d'un composant

**Objectif :** Minimiser le temps d'arrêt (downtime)

**Analogie :**
- **Sans HA** = Un seul pilote dans un avion (si le pilote a un malaise, l'avion tombe)
- **Avec HA** = Deux pilotes (si l'un a un malaise, l'autre prend le relais)

**Métriques de disponibilité :**
- **99% (SLA basique)** = ~3,65 jours d'arrêt par an
- **99.9% (three nines)** = ~8,76 heures d'arrêt par an
- **99.99% (four nines)** = ~52,6 minutes d'arrêt par an
- **99.999% (five nines)** = ~5,26 minutes d'arrêt par an

---

### Pourquoi la HA pour DHCP ?

**DHCP est un service critique :**

**Scénario 1 : Nouveau client**
```
Nouveau PC allumé
-> Envoie DHCPDISCOVER
-> Serveur DHCP down [X]
-> Pas de réponse
-> Pas d'IP
-> Pas de réseau
-> PC inutilisable
```

**Scénario 2 : Renouvellement de bail**
```
Bail expire dans 10 minutes
-> Client essaie de renouveler (DHCPREQUEST)
-> Serveur DHCP down [X]
-> Pas de renouvellement
-> Bail expire
-> Client perd son IP
-> Connexion réseau coupée
```

**Impact entreprise :**
- Nouveaux employés ne peuvent pas travailler
- Machines existantes perdent progressivement la connectivité
- Téléphones VOIP se déconnectent
- Imprimantes réseau inaccessibles
- Perte de productivité
- Coût : des milliers d'euros par heure d'arrêt

**Solution : Failover DHCP -> Zéro interruption perceptible**

---

### Types de failover DHCP

**ISC DHCP Server supporte deux modes de failover :**

#### 1. Load Balance (Répartition de charge)

**Fonctionnement :**
```
Client 1 envoie DHCPDISCOVER
├─ Primary (50% de chances) -> Répond
└─ Secondary (50% de chances) -> Répond

Les deux serveurs sont actifs simultanément
Répartition aléatoire ou alternée des requêtes
```

**Avantages :**
- [OK] Utilisation optimale des ressources (les deux serveurs travaillent)
- [OK] Performance améliorée (charge divisée par deux)
- [OK] Résilience (panne d'un serveur = 50% de capacité restante, puis 100%)

**Inconvénients :**
- [ATTENTION] Configuration plus complexe
- [ATTENTION] Synchronisation d'état nécessaire

---

#### 2. Hot Standby (Secours actif)

**Fonctionnement :**
```
Primary (actif) :
├─ Gère 100% des requêtes en temps normal
└─ Le secondary surveille

Secondary (passif) :
├─ Ne répond pas aux requêtes
├─ Reçoit la synchronisation d'état
└─ Prend le relais si primary down
```

**Avantages :**
- [OK] Configuration plus simple
- [OK] Comportement prévisible (toujours le même serveur)

**Inconvénients :**
- [ATTENTION] Secondary sous-utilisé (gaspillage de ressources)
- [ATTENTION] Performance limitée au primary

---

**Pour cet exercice, on utilisera le mode LOAD BALANCE (recommandé).**

---

### États du failover DHCP

**Le failover DHCP utilise une machine à états finis (FSM - Finite State Machine).**

**Principaux états :**

#### 1. STARTUP (Démarrage)

**État initial au démarrage du serveur.**

```
Serveur démarre
-> Charge la configuration
-> Charge les baux existants
-> État : STARTUP
```

**Le serveur ne répond PAS encore aux requêtes DHCP.**

---

#### 2. NORMAL (Fonctionnement normal)

**Les deux serveurs sont opérationnels et synchronisés.**

```
Primary : NORMAL
Secondary : NORMAL

Communication active via port 847
Synchronisation d'état en temps réel
Répartition des requêtes DHCP
```

**C'est l'état cible en production.**

---

#### 3. COMMUNICATIONS-INTERRUPTED (Communication interrompue)

**Un serveur ne peut plus communiquer avec son partenaire.**

```
Primary ne reçoit plus de réponse du Secondary
-> État : COMMUNICATIONS-INTERRUPTED
-> Continue de répondre aux requêtes
-> Garde les baux en mémoire
-> Attend le retour du partenaire
```

**Le serveur fonctionne en mode dégradé.**

**Durée :** Par défaut, si la communication n'est pas rétablie après **60 secondes**, transition vers PARTNER-DOWN.

---

#### 4. PARTNER-DOWN (Partenaire tombé)

**Un serveur déclare que son partenaire est définitivement hors service.**

```
Primary : PARTNER-DOWN
-> Prend en charge 100% des requêtes
-> Utilise TOUTE la plage d'adresses
-> Continue de synchroniser l'état (pour le retour du partenaire)

Secondary : DOWN
-> Hors service
```

**Le serveur survivant gère seul le service DHCP.**

**Transition manuelle ou automatique après un délai configuré.**

---

#### 5. POTENTIAL-CONFLICT (Conflit potentiel)

**Les deux serveurs redémarrent en même temps après une panne.**

```
Primary : Redémarre -> Charge les baux -> État POTENTIAL-CONFLICT
Secondary : Redémarre -> Charge les baux -> État POTENTIAL-CONFLICT

Problème possible :
Pendant la panne, les baux ont pu diverger
(le secondary a attribué des IPs que le primary ne connaît pas)

Résolution :
Les serveurs comparent leur état
Détectent les conflits
Se synchronisent
-> Transition vers NORMAL
```

---

#### 6. RESOLUTION-INTERRUPTED (Résolution interrompue)

**La résolution d'un conflit a échoué.**

**Rare, nécessite intervention manuelle.**

---

#### 7. RECOVER (Récupération)

**Un serveur récupère après une longue panne.**

```
Primary était en PARTNER-DOWN (seul)
Secondary revient en ligne

Secondary : État RECOVER
-> Demande l'état complet au Primary
-> Resynchronise tous les baux
-> Transition vers NORMAL
```

---

### Synchronisation d'état

**Problème à résoudre :**

```
Primary attribue 192.168.10.105 à pc-alice
Secondary ne sait rien de cette attribution

Si Primary tombe :
Secondary pourrait réattribuer 192.168.10.105 à pc-bob
-> Conflit d'IP ! [X]
```

**Solution : Synchronisation continue de l'état**

**Communication via port TCP 847 :**

```
Primary attribue 192.168.10.105 à pc-alice
v
Primary envoie UPDATE au Secondary (port 847)
Message : "192.168.10.105 attribuée à 00:0c:29:ab:cd:ef, bail expire le ..."
v
Secondary enregistre dans ses baux
v
ACK envoyé au Primary
v
Les deux serveurs ont le MÊME état
```

**Types de messages échangés :**

**POOLREQ / POOLRESP**
- Demande/réponse de plage d'adresses
- "J'ai besoin de 10 adresses supplémentaires"

**BNDUPD**
- Binding Update (mise à jour de bail)
- "192.168.10.105 attribuée à cette MAC"

**BNDACK**
- Binding Acknowledge (accusé de réception)
- "OK, j'ai enregistré le bail"

**UPDREQ / UPDREQALL**
- Update Request (demande de mise à jour)
- Lors de la resynchronisation après panne

**CONNECT / CONNECTACK**
- Établissement de la connexion failover

**STATE**
- Annonce de changement d'état
- "Je passe en PARTNER-DOWN"

---

### Partage des adresses

**En mode load balance, comment partager la plage 192.168.10.100-200 ?**

**Méthode 1 : Hash de l'adresse MAC**

```
Client avec MAC : 00:0c:29:ab:cd:ef
v
Hash (fonction mathématique) de la MAC
v
Résultat : Nombre entre 0 et 255
v
Si < 128 -> Primary répond
Si >= 128 -> Secondary répond
```

**Avantage :** Répartition statistiquement équitable (~50/50)

---

**Méthode 2 : Split (division manuelle)**

```
Plage : 192.168.10.100-200 (101 adresses)

Primary gère : 192.168.10.100-150 (51 adresses)
Secondary gère : 192.168.10.151-200 (50 adresses)

Configuration :
mclt 3600;                 # Maximum Client Lead Time
split 128;                 # 128/256 = 50% chacun
```

**`split 128`** = Répartition 50/50
- `split 255` = Primary 100%, Secondary 0%
- `split 192` = Primary 75%, Secondary 25%
- `split 64` = Primary 25%, Secondary 75%

---

**En cas de panne du Secondary :**

```
Primary : PARTNER-DOWN
-> Utilise les adresses 100-150 (sa part)
-> + Récupère progressivement 151-200 (part du Secondary)
-> Peut attribuer TOUTE la plage si nécessaire
```

---

### MCLT (Maximum Client Lead Time)

**MCLT** = Durée maximale pendant laquelle un serveur peut attribuer un bail sans accord du partenaire

**Problème :**

```
Primary attribue 192.168.10.105 pour 24h
Primary envoie UPDATE au Secondary
-> Panne réseau ! Le message ne passe pas
Secondary ne sait RIEN de cette attribution

Si Primary tombe immédiatement :
Secondary pourrait attribuer 192.168.10.105 ailleurs
-> Conflit !
```

**Solution avec MCLT :**

```
MCLT = 3600 secondes (1 heure)

Primary attribue 192.168.10.105
v
Primary offre un bail de maximum MCLT (1h) au client
v
Primary essaie d'envoyer UPDATE au Secondary
v
Si ACK reçu -> Peut étendre le bail à 24h
Si pas d'ACK -> Le client garde 1h seulement
```

**Garantie :**

Après MCLT (1h), si Secondary n'a pas reçu l'UPDATE :
- Le bail du client expire
- Le client redemande une IP
- Pas de conflit possible

**Valeur recommandée :** 1/2 de la durée de bail par défaut
- Bail par défaut = 24h -> MCLT = 12h (43200s)
- Bail par défaut = 8h -> MCLT = 4h (14400s)

**Compromis :**
- MCLT court = Sécurité accrue, mais plus de synchronisations
- MCLT long = Moins de synchronisations, mais fenêtre de conflit plus longue

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Préparer le serveur secondaire

**On suppose que tu as déjà :**
- Serveur primaire : 192.168.10.10 (configuré dans les exercices 1-3)
- Serveur secondaire : 192.168.10.11 (nouveau serveur Ubuntu 22.04)

---

**Sur le serveur SECONDAIRE (192.168.10.11) :**

**1. Mettre à jour le système :**

```bash
sudo apt update && sudo apt upgrade -y
```

---

**2. Configurer l'IP statique :**

**Vérifier l'interface réseau :**

```bash
ip addr show
```

**Résultat (exemple) :**

```
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
    inet 192.168.10.11/24 brd 192.168.10.255 scope global ens33
```

**Si l'IP n'est pas statique, la configurer :**

```bash
sudo nano /etc/netplan/00-installer-config.yaml
```

**Contenu :**

```yaml
network:
  version: 2
  ethernets:
    ens33:
      addresses:
        - 192.168.10.11/24
      routes:
        - to: default
          via: 192.168.10.1
      nameservers:
        addresses:
          - 8.8.8.8
          - 8.8.4.4
```

**Appliquer :**

```bash
sudo netplan apply
```

**Vérifier :**

```bash
ip addr show ens33 | grep inet
```

**Résultat attendu :**

```
inet 192.168.10.11/24 brd 192.168.10.255 scope global ens33
```

**[OK] IP statique configurée !**

---

**3. Installer ISC DHCP Server :**

```bash
sudo apt install isc-dhcp-server -y
```

---

**4. Configurer l'interface d'écoute :**

```bash
sudo nano /etc/default/isc-dhcp-server
```

**Contenu :**

```bash
INTERFACESv4="ens33"
INTERFACESv6=""
```

**Sauvegarde.**

---

**5. Tester la connectivité entre les deux serveurs :**

**Depuis le SECONDAIRE, ping le PRIMAIRE :**

```bash
ping -c 3 192.168.10.10
```

**Résultat attendu :**

```
3 packets transmitted, 3 received, 0% packet loss
```

**[OK] Connectivité OK !**

---

**Depuis le PRIMAIRE, ping le SECONDAIRE :**

```bash
ping -c 3 192.168.10.11
```

**Résultat attendu :**

```
3 packets transmitted, 3 received, 0% packet loss
```

**[OK] Connectivité bidirectionnelle !**

---

### ÉTAPE 2 : Générer le secret partagé pour le failover

**Le secret partagé authentifie la communication entre les deux serveurs DHCP.**

**Sur le serveur PRIMAIRE (192.168.10.10) :**

**Générer un secret aléatoire en base64 :**

```bash
dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64
```

**Explication de la commande :**

**`dd if=/dev/urandom bs=32 count=1`**
- `dd` = Data Duplicator (utilitaire de copie bas niveau)
- `if=/dev/urandom` = Input File = /dev/urandom (source de données aléatoires)
- `/dev/urandom` = Générateur de nombres pseudo-aléatoires
- `bs=32` = Block Size = 32 octets (256 bits)
- `count=1` = Copier 1 bloc

**`2>/dev/null`**
- Redirige les erreurs vers /dev/null (les ignore)
- `dd` affiche des statistiques sur stderr, on ne veut pas les voir

**`| base64`**
- Encode les 32 octets binaires en base64
- Base64 = Représentation textuelle de données binaires
- Facilite le copier/coller dans les fichiers de config

---

**Résultat (exemple) :**

```
k8mP2qR5tU7vW9xY1zB3cD4eF6gH8iJ0kL2mN4oP6qR8=
```

**[ATTENTION] COPIER CE SECRET ! On l'utilisera sur les deux serveurs.**

---

**Alternative : Générer avec OpenSSL :**

```bash
openssl rand -base64 32
```

**Résultat identique : une chaîne base64 de 44 caractères.**

---

### ÉTAPE 3 : Configurer le failover sur le serveur PRIMAIRE

**Sur le serveur PRIMAIRE (192.168.10.10) :**

**1. Sauvegarder la configuration actuelle :**

```bash
sudo cp /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd.conf.backup-ex3
```

---

**2. Éditer la configuration DHCP :**

```bash
sudo nano /etc/dhcp/dhcpd.conf
```

**AJOUTER APRÈS la configuration DDNS (avant les réservations et le subnet) :**

```conf
# ═══════════════════════════════════════════════════════════════
# CONFIGURATION FAILOVER DHCP
# ═══════════════════════════════════════════════════════════════

failover peer "dhcp-failover" {
    # ───────────────────────────────────────────────────────────
    # RÔLE DE CE SERVEUR
    # ───────────────────────────────────────────────────────────
    
    primary;
    
    # Explication :
    # primary = Ce serveur est le primaire
    # secondary = L'autre serveur est le secondaire
    #
    # La distinction primary/secondary n'affecte PAS le comportement
    # en mode load balance (les deux sont égaux)
    #
    # Elle sert seulement pour :
    # - Identifier les serveurs dans les logs
    # - Déterminer quel serveur initie la synchronisation au démarrage
    #
    # En mode load balance :
    # Primary et secondary répondent tous les deux aux requêtes
    
    # ───────────────────────────────────────────────────────────
    # ADRESSE D'ÉCOUTE DU SERVEUR PRIMARY
    # ───────────────────────────────────────────────────────────
    
    address 192.168.10.10;
    
    # Explication :
    # address = IP locale de CE serveur (primary)
    # 192.168.10.10 = IP du serveur primary
    #
    # Le serveur primary écoutera sur cette IP
    # pour recevoir les connexions du secondary
    
    # ───────────────────────────────────────────────────────────
    # PORT D'ÉCOUTE POUR LE FAILOVER
    # ───────────────────────────────────────────────────────────
    
    port 847;
    
    # Explication :
    # port = Port TCP pour la communication failover
    # 847 = Port standard pour le failover DHCP (défini par l'IANA)
    #
    # Le serveur primary écoutera sur 192.168.10.10:847
    #
    # [ATTENTION] Ce port DOIT être accessible entre les deux serveurs
    # Pare-feu : autoriser TCP 847 bidirectionnel
    
    # ───────────────────────────────────────────────────────────
    # ADRESSE DU SERVEUR PARTENAIRE (SECONDARY)
    # ───────────────────────────────────────────────────────────
    
    peer address 192.168.10.11;
    
    # Explication :
    # peer address = IP du serveur partenaire (secondary)
    # 192.168.10.11 = IP du secondary
    #
    # Le primary se connectera à 192.168.10.11:847
    # pour synchroniser l'état
    
    # ───────────────────────────────────────────────────────────
    # PORT DU SERVEUR PARTENAIRE
    # ───────────────────────────────────────────────────────────
    
    peer port 847;
    
    # Explication :
    # peer port = Port TCP du partenaire
    # 847 = Le secondary écoute aussi sur le port 847
    #
    # Communication :
    # Primary : 192.168.10.10:847 <--> Secondary : 192.168.10.11:847
    
    # ───────────────────────────────────────────────────────────
    # MCLT (MAXIMUM CLIENT LEAD TIME)
    # ───────────────────────────────────────────────────────────
    
    max-client-lead-time 3600;
    
    # Explication :
    # max-client-lead-time = MCLT en secondes
    # 3600 secondes = 1 heure
    #
    # MCLT = Durée maximale pendant laquelle un serveur peut
    #        attribuer un bail sans confirmation du partenaire
    #
    # Scénario :
    # 1. Primary attribue une IP pour 24h (default-lease-time)
    # 2. Primary offre au client : min(24h, MCLT) = 1h
    # 3. Primary envoie UPDATE au secondary
    # 4. Si ACK reçu -> Peut étendre le bail à 24h
    # 5. Si pas d'ACK -> Le client garde 1h seulement
    #
    # Après 1h sans ACK :
    # - Le bail du client expire
    # - Pas de conflit possible avec le secondary
    #
    # Valeur recommandée :
    # MCLT = default-lease-time / 2
    # Si default-lease-time = 86400s (24h) -> MCLT = 43200s (12h)
    # Si default-lease-time = 7200s (2h) -> MCLT = 3600s (1h)
    #
    # [ATTENTION] MCLT ne peut être configuré QUE sur le primary !
    # Le secondary héritera de cette valeur
    
    # ───────────────────────────────────────────────────────────
    # RÉPARTITION DE CHARGE (LOAD BALANCING)
    # ───────────────────────────────────────────────────────────
    
    split 128;
    
    # Explication :
    # split = Répartition des requêtes entre primary et secondary
    # Valeur : 0 à 255
    #
    # split 128 :
    # - 128/256 = 50% pour le primary
    # - (256-128)/256 = 50% pour le secondary
    # - Répartition équitable
    #
    # Autres exemples :
    # split 255 = 100% primary, 0% secondary (hot standby)
    # split 192 = 75% primary, 25% secondary
    # split 64 = 25% primary, 75% secondary
    # split 0 = 0% primary, 100% secondary
    #
    # Algorithme de répartition :
    # 1. Hash de l'adresse MAC du client -> Nombre entre 0 et 255
    # 2. Si hash < split (128) -> Primary répond
    # 3. Si hash >= split (128) -> Secondary répond
    #
    # Résultat : Distribution statistiquement équitable
    #
    # [ATTENTION] split ne peut être configuré QUE sur le primary !
    
    # ───────────────────────────────────────────────────────────
    # MODE DE RÉPARTITION
    # ───────────────────────────────────────────────────────────
    
    load balance max seconds 3;
    
    # Explication :
    # load balance max seconds = Durée max avant de répondre
    #
    # Processus de réponse en load balance :
    # 1. Client envoie DHCPDISCOVER (broadcast)
    # 2. Les DEUX serveurs reçoivent la requête
    # 3. Hash de la MAC -> Détermine qui doit répondre
    # 4. Le serveur "gagnant" répond immédiatement (DHCPOFFER)
    # 5. Le serveur "perdant" attend max seconds (3s)
    # 6. Si pas de DHCPOFFER du gagnant -> Le perdant répond (backup)
    #
    # Scénario normal :
    # - Hash détermine : Primary doit répondre
    # - Primary envoie DHCPOFFER immédiatement
    # - Secondary attend 3s, voit la réponse du primary, abandonne
    #
    # Scénario de panne :
    # - Hash détermine : Primary doit répondre
    # - Primary est down [X]
    # - Secondary attend 3s
    # - Pas de DHCPOFFER détecté
    # - Secondary envoie DHCPOFFER (secours)
    # - Client obtient une IP ! [OK]
    #
    # Valeur recommandée : 3 secondes
    # - Assez court pour réactivité
    # - Assez long pour détecter une panne

    # ───────────────────────────────────────────────────────────
    # DÉLAI AVANT PARTNER-DOWN AUTOMATIQUE
    # ───────────────────────────────────────────────────────────
    
    max-response-delay 60;
    
    # Explication :
    # max-response-delay = Délai avant de considérer le partenaire mort
    # 60 secondes
    #
    # Processus :
    # 1. Primary n'arrive plus à communiquer avec secondary (port 847)
    # 2. Primary : État COMMUNICATIONS-INTERRUPTED
    # 3. Primary attend max-response-delay (60s)
    # 4. Si toujours pas de réponse -> Primary : État PARTNER-DOWN
    # 5. Primary prend en charge 100% des requêtes
    #
    # Valeur recommandée : 60 secondes
    # - Assez long pour éviter les faux positifs (coupure réseau temporaire)
    # - Assez court pour basculer rapidement en cas de vraie panne
    #
    # Trop court (ex: 5s) :
    # - Risque de basculement lors d'une micro-coupure réseau
    # - "Split-brain" possible (les deux serveurs pensent être seuls)
    #
    # Trop long (ex: 300s) :
    # - Attente trop longue avant basculement
    # - Service dégradé pendant 5 minutes
    
    # ───────────────────────────────────────────────────────────
    # DÉLAI AVANT UNRESPONSIVE
    # ───────────────────────────────────────────────────────────
    
    max-unacked-updates 10;
    
    # Explication :
    # max-unacked-updates = Nombre max de mises à jour sans ACK
    #
    # Processus de synchronisation :
    # 1. Primary attribue une IP
    # 2. Primary envoie BNDUPD au secondary
    # 3. Secondary répond BNDACK
    #
    # Si le secondary ne répond pas :
    # 1. Primary conserve le BNDUPD en mémoire (queue)
    # 2. Après 10 BNDUPD sans ACK
    # 3. Primary considère le secondary "unresponsive"
    # 4. Transition vers COMMUNICATIONS-INTERRUPTED
    #
    # Valeur recommandée : 10
    # - Évite de saturer la mémoire avec des updates en attente
    # - Détecte rapidement un problème de communication
    
    # ───────────────────────────────────────────────────────────
    # SECRET PARTAGÉ (AUTHENTIFICATION)
    # ───────────────────────────────────────────────────────────
    
    mclt 3600;
    
    # Explication :
    # mclt = Alias pour max-client-lead-time (même fonction)
    # Répétition pour compatibilité avec anciennes versions
    #
    # [ATTENTION] Certaines versions d'ISC DHCP utilisent "mclt"
    # au lieu de "max-client-lead-time"
    # Utilise la syntaxe supportée par ta version
    
    # Secret partagé pour authentifier la communication
    # [ATTENTION] REMPLACER par le secret généré à l'étape 2 !
    load balance max seconds 3;
}

# ═══════════════════════════════════════════════════════════════
```

**[ATTENTION] ATTENTION : Remplacer le secret par celui généré précédemment !**

**Exemple :**

```conf
failover peer "dhcp-failover" {
    primary;
    address 192.168.10.10;
    port 847;
    peer address 192.168.10.11;
    peer port 847;
    max-client-lead-time 3600;
    split 128;
    load balance max seconds 3;
    max-response-delay 60;
    max-unacked-updates 10;
}
```

---

**3. Modifier la déclaration du subnet pour utiliser le failover :**

**Trouver le bloc `subnet 192.168.10.0` et MODIFIER les pools :**

```conf
subnet 192.168.10.0 netmask 255.255.255.0 {
    
    # Options globales (identiques à avant)
    option subnet-mask 255.255.255.0;
    option routers 192.168.10.1;
    option domain-name "entreprise.local";
    option domain-name-servers 192.168.10.10, 8.8.8.8;
    
    default-lease-time 86400;
    max-lease-time 86400;
    
    # ───────────────────────────────────────────────────────────
    # POOL AVEC FAILOVER
    # ───────────────────────────────────────────────────────────
    
    pool {
        # Activer le failover pour ce pool
        failover peer "dhcp-failover";
        
        # Explication :
        # failover peer "dhcp-failover";
        # - Active le failover pour ce pool
        # - Référence la configuration failover définie plus haut
        # - "dhcp-failover" = Nom du peer (doit correspondre)
        #
        # Sans cette ligne :
        # Le pool fonctionne normalement (sans failover)
        #
        # Avec cette ligne :
        # - Les deux serveurs gèrent ce pool en tandem
        # - Synchronisation d'état automatique
        # - Répartition selon split (50/50)
        
        # Plage d'adresses partagée entre les deux serveurs
        range 192.168.10.100 192.168.10.200;
        
        # Explication :
        # range = Plage TOTALE gérée par le failover
        # 192.168.10.100-200 = 101 adresses
        #
        # Répartition dynamique selon split :
        # - Primary gère ~50 adresses
        # - Secondary gère ~51 adresses
        #
        # En cas de panne du secondary :
        # - Primary récupère progressivement les 51 adresses du secondary
        # - Peut utiliser TOUTE la plage si nécessaire
        #
        # La répartition est automatique, pas de découpage manuel !
    }
}
```

**[ATTENTION] Important :**
- Ne PAS définir plusieurs `range` (une seule suffit pour le failover)
- La même `range` sera utilisée sur les deux serveurs
- Les pools séparés par département (exercice 2) peuvent coexister avec le failover

---

**4. Sauvegarder : `Ctrl + O`, `Entrée`, `Ctrl + X`**

---

**5. Vérifier la syntaxe :**

```bash
sudo dhcpd -t -cf /etc/dhcp/dhcpd.conf
```

**Résultat attendu : pas d'erreur.**

---

**6. NE PAS redémarrer le service pour l'instant** (on configure d'abord le secondary).

---

### ÉTAPE 4 : Configurer le failover sur le serveur SECONDAIRE

**Sur le serveur SECONDAIRE (192.168.10.11) :**

**1. Éditer la configuration DHCP :**

```bash
sudo nano /etc/dhcp/dhcpd.conf
```

**Contenu COMPLET (à adapter selon ta configuration DDNS de l'exercice 3) :**

```conf
# ═══════════════════════════════════════════════════════════════
# CONFIGURATION DHCP SECONDAIRE (FAILOVER)
# ═══════════════════════════════════════════════════════════════

# ───────────────────────────────────────────────────────────────
# OPTIONS GLOBALES
# ───────────────────────────────────────────────────────────────

authoritative;
default-lease-time 86400;
max-lease-time 86400;

# ───────────────────────────────────────────────────────────────
# CONFIGURATION DNS DYNAMIQUE (identique au primary)
# ───────────────────────────────────────────────────────────────

# Inclure la clé TSIG (copier depuis le primary)
include "/etc/bind/ddns.key";

ddns-update-style standard;
ignore client-updates;
update-static-leases on;
ddns-domainname "entreprise.local";
ddns-rev-domainname "in-addr.arpa";

zone entreprise.local. {
    primary 192.168.10.10;
    key ddns-key;
}

zone 10.168.192.in-addr.arpa. {
    primary 192.168.10.10;
    key ddns-key;
}

# ═══════════════════════════════════════════════════════════════
# CONFIGURATION FAILOVER DHCP
# ═══════════════════════════════════════════════════════════════

failover peer "dhcp-failover" {
    # ───────────────────────────────────────────────────────────
    # RÔLE DE CE SERVEUR : SECONDAIRE
    # ───────────────────────────────────────────────────────────
    
    secondary;
    
    # Explication :
    # secondary = Ce serveur est le secondaire
    #
    # Différence avec primary :
    # - Le primary peut définir : mclt, split
    # - Le secondary HÉRITE de ces valeurs (ne doit PAS les définir)
    #
    # En mode load balance, comportement identique au primary
    
    # ───────────────────────────────────────────────────────────
    # ADRESSE D'ÉCOUTE DU SERVEUR SECONDAIRE
    # ───────────────────────────────────────────────────────────
    
    address 192.168.10.11;
    
    # Explication :
    # address = IP locale de CE serveur (secondary)
    # 192.168.10.11 = IP du serveur secondary
    #
    # Le secondary écoutera sur 192.168.10.11:847
    
    # ───────────────────────────────────────────────────────────
    # PORT D'ÉCOUTE
    # ───────────────────────────────────────────────────────────
    
    port 847;
    
    # ───────────────────────────────────────────────────────────
    # ADRESSE DU PARTENAIRE (PRIMARY)
    # ───────────────────────────────────────────────────────────
    
    peer address 192.168.10.10;
    
    # Explication :
    # peer address = IP du serveur partenaire (primary)
    # 192.168.10.10 = IP du primary
    
    # ───────────────────────────────────────────────────────────
    # PORT DU PARTENAIRE
    # ───────────────────────────────────────────────────────────
    
    peer port 847;
    
    # ───────────────────────────────────────────────────────────
    # MODE DE RÉPARTITION
    # ───────────────────────────────────────────────────────────
    
    load balance max seconds 3;
    
    # ───────────────────────────────────────────────────────────
    # DÉLAI AVANT PARTNER-DOWN
    # ───────────────────────────────────────────────────────────
    
    max-response-delay 60;
    
    # ───────────────────────────────────────────────────────────
    # DÉLAI AVANT UNRESPONSIVE
    # ───────────────────────────────────────────────────────────
    
    max-unacked-updates 10;
    
    # [ATTENTION] NE PAS définir mclt ni split sur le secondary !
    # Ces valeurs sont héritées du primary automatiquement
}

# ═══════════════════════════════════════════════════════════════
# RÉSERVATIONS (identiques au primary)
# ═══════════════════════════════════════════════════════════════

# Copier toutes les réservations host { ... } du primary
# Exemple :

host serveur-fichiers {
    hardware ethernet 00:11:22:33:44:55;
    fixed-address 192.168.10.5;
    option host-name "srv-fichiers";
}

host serveur-web {
    hardware ethernet 00:FF:EE:DD:CC:BB;
    fixed-address 192.168.10.7;
    option host-name "srv-web";
}

host imprimante-rh {
    hardware ethernet 00:AA:BB:CC:DD:EE;
    fixed-address 192.168.10.6;
    option host-name "print-rh";
    default-lease-time 604800;
    max-lease-time 604800;
}

# ═══════════════════════════════════════════════════════════════
# DÉCLARATION DU SOUS-RÉSEAU
# ═══════════════════════════════════════════════════════════════

subnet 192.168.10.0 netmask 255.255.255.0 {
    
    # Options globales
    option subnet-mask 255.255.255.0;
    option routers 192.168.10.1;
    option domain-name "entreprise.local";
    option domain-name-servers 192.168.10.10, 8.8.8.8;
    
    default-lease-time 86400;
    max-lease-time 86400;
    
    # Pool avec failover
    pool {
        failover peer "dhcp-failover";
        
        # [ATTENTION] MÊME plage que sur le primary !
        range 192.168.10.100 192.168.10.200;
    }
}

# ═══════════════════════════════════════════════════════════════
```

**Points critiques :**

**1. `secondary;` au lieu de `primary;`**

**2. `address 192.168.10.11;` (IP de CE serveur)**

**3. `peer address 192.168.10.10;` (IP du primary)**

**4. PAS de `mclt` ni `split` (hérités du primary)**

**5. MÊME plage : `range 192.168.10.100 192.168.10.200;`**

---

**2. Copier la clé DDNS depuis le primary :**

**Sur le PRIMARY (192.168.10.10) :**

```bash
sudo cat /etc/bind/ddns.key
```

**Copier le contenu (exemple) :**

```
key "ddns-key" {
	algorithm hmac-sha256;
	secret "vJZ3fK8mN2pQ5rT7wX9yA1bC3dE4fG6hI8jK0lM2nO4=";
};
```

---

**Sur le SECONDARY (192.168.10.11) :**

```bash
sudo mkdir -p /etc/bind
sudo nano /etc/bind/ddns.key
```

**Coller le contenu copié.**

**Sauvegarder.**

**Sécuriser :**

```bash
sudo chown root:root /etc/bind/ddns.key
sudo chmod 640 /etc/bind/ddns.key
```

---

**3. Sauvegarder la configuration DHCP.**

---

**4. Vérifier la syntaxe :**

```bash
sudo dhcpd -t -cf /etc/dhcp/dhcpd.conf
```

**Résultat attendu : pas d'erreur.**

---

### ÉTAPE 5 : Autoriser le port 847 dans le pare-feu

**Le failover communique via le port TCP 847. Il faut l'autoriser sur les DEUX serveurs.**

**Sur le PRIMARY (192.168.10.10) :**

```bash
# Vérifier si le pare-feu est actif
sudo ufw status
```

**Si actif, autoriser le port 847 :**

```bash
sudo ufw allow from 192.168.10.11 to any port 847 proto tcp comment 'DHCP Failover from Secondary'
```

**Explication :**
- `allow from 192.168.10.11` = Autoriser depuis le secondary
- `to any port 847` = Vers le port 847
- `proto tcp` = Protocole TCP
- `comment '...'` = Commentaire pour documentation

---

**Sur le SECONDARY (192.168.10.11) :**

```bash
sudo ufw allow from 192.168.10.10 to any port 847 proto tcp comment 'DHCP Failover from Primary'
```

---

**Vérifier les règles :**

```bash
sudo ufw status numbered
```

**Résultat (exemple) :**

```
     To                         Action      From
     --                         ------      ----
[ 1] 847/tcp                    ALLOW IN    192.168.10.11  # DHCP Failover from Secondary
```

---

### ÉTAPE 6 : Démarrer les serveurs DHCP en failover

**L'ordre de démarrage est IMPORTANT !**

**1. Démarrer le PRIMARY en premier :**

**Sur le PRIMARY (192.168.10.10) :**

```bash
sudo systemctl restart isc-dhcp-server
```

**Vérifier l'état :**

```bash
sudo systemctl status isc-dhcp-server
```

**Résultat attendu : `Active: active (running)`**

---

**Consulter les logs :**

```bash
sudo tail -20 /var/log/syslog | grep dhcpd
```

**Résultat attendu :**

```
dhcpd[12345]: Internet Systems Consortium DHCP Server 4.4.1
dhcpd[12345]: Copyright 2004-2018 Internet Systems Consortium.
dhcpd[12345]: All rights reserved.
dhcpd[12345]: For info, please visit https://www.isc.org/software/dhcp/
dhcpd[12345]: failover peer dhcp-failover: I move from startup to normal
dhcpd[12345]: failover peer dhcp-failover: peer moves from startup to normal
dhcpd[12345]: Listening on LPF/ens33/...
dhcpd[12345]: Sending on   LPF/ens33/...
dhcpd[12345]: Server starting service.
```

**Point important :**

**`failover peer dhcp-failover: I move from startup to normal`**
- [OK] Le primary est en état NORMAL

**Pour l'instant, le secondary n'est pas encore démarré, donc le primary attend.**

---

**2. Démarrer le SECONDARY :**

**Sur le SECONDARY (192.168.10.11) :**

```bash
sudo systemctl start isc-dhcp-server
```

**Vérifier l'état :**

```bash
sudo systemctl status isc-dhcp-server
```

**Résultat attendu : `Active: active (running)`**

---

**Consulter les logs :**

```bash
sudo tail -30 /var/log/syslog | grep dhcpd
```

**Résultat attendu :**

```
dhcpd[23456]: Internet Systems Consortium DHCP Server 4.4.1
dhcpd[23456]: failover peer dhcp-failover: I move from startup to normal
dhcpd[23456]: failover peer dhcp-failover: peer moves from startup to normal
dhcpd[23456]: Listening on LPF/ens33/...
dhcpd[23456]: Sending on   LPF/ens33/...
dhcpd[23456]: Server starting service.
```

**[OK] Le secondary est en état NORMAL !**

---

**3. Re-vérifier les logs du PRIMARY :**

```bash
sudo tail -10 /var/log/syslog | grep dhcpd
```

**Résultat attendu :**

```
dhcpd[12345]: failover peer dhcp-failover: peer moves from startup to normal
dhcpd[12345]: failover peer dhcp-failover: I move from startup to normal
```

**[OK] Les deux serveurs sont synchronisés et en état NORMAL !**

---

**4. Activer le démarrage automatique (sur les deux serveurs) :**

```bash
sudo systemctl enable isc-dhcp-server
```

---

Je continue avec les tests du failover (ÉTAPE 7) ?

### ÉTAPE 7 : Tester le failover DHCP

**Maintenant que le failover est configuré, testons tous les scénarios !**

---

#### Test 1 : Vérifier l'état du failover

**Sur le PRIMARY (192.168.10.10) :**

**Vérifier l'état avec `omshell` (OMAPI Shell) :**

```bash
omshell
```

**`omshell`** = Outil interactif pour interroger le serveur DHCP via l'API de management (OMAPI)

**Commandes à taper dans omshell :**

```
> server 127.0.0.1
> port 7911
> connect
```

**Explication :**
- `server 127.0.0.1` = Se connecter au serveur DHCP local
- `port 7911` = Port OMAPI (par défaut)
- `connect` = Établir la connexion

**Résultat attendu :**

```
obj: <null>
```

**[OK] Connexion établie !**

---

**Interroger l'état du failover :**

```
> new failover-state
> set name = "dhcp-failover"
> open
```

**Explication :**
- `new failover-state` = Créer un objet failover-state
- `set name = "dhcp-failover"` = Nom du peer défini dans dhcpd.conf
- `open` = Interroger l'état

**Résultat attendu :**

```
obj: failover-state
name = "dhcp-failover"
partner-address = c0:a8:0a:0b
local-address = c0:a8:0a:0a
partner-port = 847
local-port = 847
max-outstanding-updates = 10
mclt = 3600
load-balance-max-secs = 3
local-state = 02
partner-state = 02
local-stos = 1702939200
partner-stos = 1702939200
```

**Points importants :**

**`partner-address = c0:a8:0a:0b`**
- c0:a8:0a:0b = 192.168.10.11 en hexadécimal
- Conversion : c0=192, a8=168, 0a=10, 0b=11
- [OK] Adresse du secondary correcte

**`local-address = c0:a8:0a:0a`**
- c0:a8:0a:0a = 192.168.10.10 en hexadécimal
- [OK] Adresse du primary correcte

**`local-state = 02`**
- 02 = NORMAL (en hexadécimal)
- [OK] Primary en état NORMAL

**`partner-state = 02`**
- 02 = NORMAL
- [OK] Secondary en état NORMAL

**États possibles (codes hexadécimaux) :**
- `01` = STARTUP
- `02` = NORMAL
- `03` = COMMUNICATIONS-INTERRUPTED
- `04` = PARTNER-DOWN
- `05` = POTENTIAL-CONFLICT
- `06` = RECOVER
- `07` = PAUSED
- `08` = SHUTDOWN
- `09` = RESOLUTION-INTERRUPTED
- `0a` = CONFLICT-DONE

**`mclt = 3600`**
- [OK] MCLT = 1 heure (3600s)

**`load-balance-max-secs = 3`**
- [OK] Délai max = 3 secondes

---

**Quitter omshell :**

```
> quit
```

---

**Alternative : Consulter les logs**

```bash
sudo journalctl -u isc-dhcp-server -n 50 | grep failover
```

**Résultat (exemple) :**

```
Dec 16 23:45:00 dhcp-primary dhcpd[12345]: failover peer dhcp-failover: I move from startup to normal
Dec 16 23:45:01 dhcp-primary dhcpd[12345]: failover peer dhcp-failover: peer moves from startup to normal
```

**[OK] Failover opérationnel !**

---

#### Test 2 : Attribution d'IP en mode normal (load balance)

**Connecter plusieurs clients pour vérifier la répartition de charge.**

**Client 1 : pc-alice**

**Sur une machine cliente (ou VM) :**

```bash
sudo hostnamectl set-hostname pc-alice
sudo dhclient -r ens33
sudo dhclient -v ens33
```

**Résultat (exemple) :**

```
DHCPDISCOVER on ens33
DHCPOFFER of 192.168.10.105 from 192.168.10.10
DHCPREQUEST for 192.168.10.105
DHCPACK of 192.168.10.105 from 192.168.10.10
bound to 192.168.10.105
```

**[OK] IP attribuée par le PRIMARY (192.168.10.10)**

---

**Vérifier sur le PRIMARY :**

```bash
sudo tail -10 /var/log/syslog | grep dhcpd
```

**Résultat attendu :**

```
dhcpd[12345]: DHCPDISCOVER from 00:0c:29:ab:cd:01 via ens33
dhcpd[12345]: DHCPOFFER on 192.168.10.105 to 00:0c:29:ab:cd:01 via ens33
dhcpd[12345]: DHCPREQUEST for 192.168.10.105 from 00:0c:29:ab:cd:01 via ens33
dhcpd[12345]: DHCPACK on 192.168.10.105 to 00:0c:29:ab:cd:01 via ens33
dhcpd[12345]: failover peer dhcp-failover: update queue: 1 pending, max 10
```

**Point important :**

**`failover peer dhcp-failover: update queue: 1 pending`**
- Le primary envoie une mise à jour (BNDUPD) au secondary
- 1 mise à jour en attente d'ACK

---

**Vérifier sur le SECONDARY :**

```bash
sudo tail -10 /var/log/syslog | grep dhcpd
```

**Résultat attendu :**

```
dhcpd[23456]: failover peer dhcp-failover: Got BNDUPD from 192.168.10.10
dhcpd[23456]: failover peer dhcp-failover: send update for 192.168.10.105
dhcpd[23456]: failover peer dhcp-failover: Sent BNDACK for 192.168.10.105
```

**Points importants :**

**`Got BNDUPD from 192.168.10.10`**
- [OK] Secondary a reçu la mise à jour du primary

**`Sent BNDACK for 192.168.10.105`**
- [OK] Secondary a confirmé l'enregistrement du bail

**[OK] Synchronisation d'état fonctionne !**

---

**Client 2 : pc-bob**

**Sur une autre machine cliente :**

```bash
sudo hostnamectl set-hostname pc-bob
sudo dhclient -v ens33
```

**Résultat (exemple) :**

```
DHCPDISCOVER on ens33
DHCPOFFER of 192.168.10.155 from 192.168.10.11
DHCPREQUEST for 192.168.10.155
DHCPACK of 192.168.10.155 from 192.168.10.11
bound to 192.168.10.155
```

**[OK] IP attribuée par le SECONDARY (192.168.10.11)**

**-> Load balancing fonctionne ! Les deux serveurs répondent.**

---

**Continuer avec plusieurs clients :**

**Client 3 : pc-charlie**

```bash
sudo hostnamectl set-hostname pc-charlie
sudo dhclient -v ens33
```

**Noter quel serveur répond (primary ou secondary).**

---

**Après 10-20 clients, vérifier la répartition :**

**Compter les attributions par serveur :**

```bash
# Sur le PRIMARY
sudo grep "DHCPACK on" /var/log/syslog | grep "from 192.168.10.10" | wc -l

# Sur le SECONDARY
sudo grep "DHCPACK on" /var/log/syslog | grep "from 192.168.10.11" | wc -l
```

**Résultat attendu :**

```
Primary : 11 attributions
Secondary : 9 attributions
```

**[OK] Répartition ~50/50 !**

---

#### Test 3 : Vérifier la synchronisation des baux

**Les deux serveurs doivent avoir la MÊME liste de baux actifs.**

**Sur le PRIMARY :**

```bash
sudo cat /var/lib/dhcp/dhcpd.leases | grep "binding state active" -B 5 | grep "lease " | awk '{print $2}' | sort -V
```

**Résultat (exemple) :**

```
192.168.10.105
192.168.10.106
192.168.10.155
192.168.10.156
192.168.10.157
```

---

**Sur le SECONDARY :**

```bash
sudo cat /var/lib/dhcp/dhcpd.leases | grep "binding state active" -B 5 | grep "lease " | awk '{print $2}' | sort -V
```

**Résultat (DOIT être identique) :**

```
192.168.10.105
192.168.10.106
192.168.10.155
192.168.10.156
192.168.10.157
```

**[OK] Les deux serveurs ont la même liste de baux !**

**Si différent -> Problème de synchronisation (voir erreurs courantes plus bas)**

---

#### Test 4 : Simuler une panne du PRIMARY

**Ce test vérifie que le SECONDARY prend le relais en cas de panne du PRIMARY.**

**1. Arrêter le PRIMARY :**

**Sur le PRIMARY (192.168.10.10) :**

```bash
sudo systemctl stop isc-dhcp-server
```

**Le primary est maintenant DOWN [X]**

---

**2. Observer les logs du SECONDARY :**

```bash
sudo tail -f /var/log/syslog | grep dhcpd
```

**Résultat attendu (après ~60 secondes) :**

```
dhcpd[23456]: failover peer dhcp-failover: peer moves from normal to communications-interrupted
dhcpd[23456]: failover peer dhcp-failover: I move from normal to communications-interrupted
[...attente de 60 secondes (max-response-delay)...]
dhcpd[23456]: failover peer dhcp-failover: peer moves from communications-interrupted to partner-down
dhcpd[23456]: failover peer dhcp-failover: I move from communications-interrupted to partner-down
```

**Explication du processus :**

**T+0s : Primary arrêté**
- Secondary envoie des messages sur le port 847
- Pas de réponse

**T+5s : COMMUNICATIONS-INTERRUPTED**
- Secondary : "Je ne peux plus communiquer avec le primary"
- État : COMMUNICATIONS-INTERRUPTED
- Continue de répondre aux requêtes (sa part : 50%)

**T+60s : PARTNER-DOWN**
- Secondary : "Le primary est mort"
- État : PARTNER-DOWN
- Prend en charge 100% des requêtes
- Utilise TOUTE la plage (192.168.10.100-200)

---

**3. Connecter un nouveau client :**

**Sur une machine cliente :**

```bash
sudo hostnamectl set-hostname pc-test-failover
sudo dhclient -v ens33
```

**Résultat attendu :**

```
DHCPDISCOVER on ens33
DHCPOFFER of 192.168.10.112 from 192.168.10.11
DHCPREQUEST for 192.168.10.112
DHCPACK of 192.168.10.112 from 192.168.10.11
bound to 192.168.10.112
```

**[OK] Le SECONDARY répond seul !**

**Même si le hash de la MAC désignerait normalement le PRIMARY, le SECONDARY prend le relais.**

---

**4. Vérifier l'état du failover sur le SECONDARY :**

```bash
omshell
> server 127.0.0.1
> connect
> new failover-state
> set name = "dhcp-failover"
> open
```

**Résultat attendu :**

```
local-state = 04
partner-state = ff
```

**`local-state = 04`**
- 04 = PARTNER-DOWN
- [OK] Secondary en mode PARTNER-DOWN (gère seul le service)

**`partner-state = ff`**
- ff = UNKNOWN/DOWN
- [OK] Primary est considéré hors service

---

**5. Tester plusieurs attributions :**

**Connecter 5-10 nouveaux clients.**

**Tous doivent obtenir une IP du SECONDARY (192.168.10.11).**

```bash
# Sur chaque client
sudo dhclient -v ens33 | grep "from"
```

**Résultat attendu :**

```
DHCPOFFER of X.X.X.X from 192.168.10.11
DHCPACK of X.X.X.X from 192.168.10.11
```

**[OK] Le SECONDARY gère seul 100% des requêtes !**

---

#### Test 5 : Retour du PRIMARY (récupération)

**Ce test vérifie que le PRIMARY se resynchronise correctement après une panne.**

**1. Redémarrer le PRIMARY :**

**Sur le PRIMARY (192.168.10.10) :**

```bash
sudo systemctl start isc-dhcp-server
```

---

**2. Observer les logs du PRIMARY :**

```bash
sudo tail -f /var/log/syslog | grep dhcpd
```

**Résultat attendu :**

```
dhcpd[12345]: Internet Systems Consortium DHCP Server 4.4.1
dhcpd[12345]: failover peer dhcp-failover: I move from startup to recover
dhcpd[12345]: failover peer dhcp-failover: requesting full update from peer
dhcpd[12345]: failover peer dhcp-failover: Got POOLRESP from 192.168.10.11
dhcpd[12345]: failover peer dhcp-failover: Got BNDUPD from 192.168.10.11 (resync)
dhcpd[12345]: failover peer dhcp-failover: Got BNDUPD from 192.168.10.11 (resync)
[...]
dhcpd[12345]: failover peer dhcp-failover: resync complete
dhcpd[12345]: failover peer dhcp-failover: I move from recover to normal
dhcpd[12345]: failover peer dhcp-failover: peer moves from partner-down to normal
```

**Explication du processus :**

**État RECOVER**
- Primary : "Je reviens après une panne, je ne connais pas l'état actuel"
- Primary demande au secondary : "Donne-moi TOUS tes baux"

**Resynchronisation**
- Secondary envoie TOUS les baux (BNDUPD)
- Primary reconstruit sa base de données
- Peut prendre quelques secondes à quelques minutes selon le nombre de baux

**Transition vers NORMAL**
- Primary : État NORMAL
- Secondary : État NORMAL
- Load balancing reprend normalement (50/50)

---

**3. Observer les logs du SECONDARY :**

```bash
sudo tail -f /var/log/syslog | grep dhcpd
```

**Résultat attendu :**

```
dhcpd[23456]: failover peer dhcp-failover: peer moves from unknown to recover
dhcpd[23456]: failover peer dhcp-failover: Got UPDREQALL from 192.168.10.10
dhcpd[23456]: failover peer dhcp-failover: sending all bindings to peer
dhcpd[23456]: failover peer dhcp-failover: Sent BNDUPD (resync) for 192.168.10.105
dhcpd[23456]: failover peer dhcp-failover: Sent BNDUPD (resync) for 192.168.10.106
[...]
dhcpd[23456]: failover peer dhcp-failover: resync complete
dhcpd[23456]: failover peer dhcp-failover: I move from partner-down to normal
dhcpd[23456]: failover peer dhcp-failover: peer moves from recover to normal
```

**[OK] Resynchronisation complète !**

---

**4. Vérifier que les deux serveurs ont le même état :**

**Primary :**

```bash
sudo cat /var/lib/dhcp/dhcpd.leases | grep "binding state active" -B 5 | grep "lease " | awk '{print $2}' | sort -V
```

**Secondary :**

```bash
sudo cat /var/lib/dhcp/dhcpd.leases | grep "binding state active" -B 5 | grep "lease " | awk '{print $2}' | sort -V
```

**Les deux listes DOIVENT être identiques.**

**[OK] Synchronisation complète après récupération !**

---

**5. Tester le load balancing :**

**Connecter de nouveaux clients.**

**Vérifier que les deux serveurs répondent à nouveau (~50/50).**

```bash
# Client 1
sudo dhclient -v ens33 | grep "from"
# Peut obtenir une réponse de .10 ou .11

# Client 2
sudo dhclient -v ens33 | grep "from"
# Peut obtenir une réponse de .10 ou .11
```

**[OK] Load balancing restauré !**

---

#### Test 6 : Simuler une panne du SECONDARY

**Ce test vérifie que le PRIMARY prend le relais en cas de panne du SECONDARY.**

**1. Arrêter le SECONDARY :**

**Sur le SECONDARY (192.168.10.11) :**

```bash
sudo systemctl stop isc-dhcp-server
```

---

**2. Observer les logs du PRIMARY :**

```bash
sudo tail -f /var/log/syslog | grep dhcpd
```

**Résultat attendu (après ~60 secondes) :**

```
dhcpd[12345]: failover peer dhcp-failover: peer moves from normal to communications-interrupted
dhcpd[12345]: failover peer dhcp-failover: I move from normal to communications-interrupted
[...attente...]
dhcpd[12345]: failover peer dhcp-failover: peer moves from communications-interrupted to partner-down
dhcpd[12345]: failover peer dhcp-failover: I move from communications-interrupted to partner-down
```

**[OK] PRIMARY en mode PARTNER-DOWN (gère seul le service)**

---

**3. Connecter de nouveaux clients :**

**Tous doivent obtenir une IP du PRIMARY (192.168.10.10).**

```bash
sudo dhclient -v ens33 | grep "from"
```

**Résultat attendu :**

```
from 192.168.10.10
```

**[OK] PRIMARY gère 100% des requêtes !**

---

**4. Redémarrer le SECONDARY :**

```bash
sudo systemctl start isc-dhcp-server
```

**Observer la resynchronisation (identique au test 5).**

**[OK] Retour à la normale !**

---

#### Test 7 : Redémarrage simultané (POTENTIAL-CONFLICT)

**Ce test vérifie la résolution de conflits après un redémarrage simultané.**

**1. Arrêter les DEUX serveurs :**

**Primary :**

```bash
sudo systemctl stop isc-dhcp-server
```

**Secondary :**

```bash
sudo systemctl stop isc-dhcp-server
```

---

**2. Modifier manuellement les baux pour créer un conflit (optionnel) :**

**Sur le PRIMARY, éditer `/var/lib/dhcp/dhcpd.leases` :**

**Ajouter un bail fictif :**

```bash
sudo nano /var/lib/dhcp/dhcpd.leases
```

**Ajouter à la fin :**

```
lease 192.168.10.199 {
  starts 6 2024/12/17 00:00:00;
  ends 0 2024/12/18 00:00:00;
  cltt 6 2024/12/17 00:00:00;
  binding state active;
  next binding state free;
  hardware ethernet 11:22:33:44:55:66;
  uid "\001\021\"3DUf";
  client-hostname "pc-conflict-test";
}
```

**Sauvegarder.**

**Ce bail n'existe PAS sur le secondary -> Conflit potentiel.**

---

**3. Démarrer les DEUX serveurs en même temps :**

**Terminal 1 (Primary) :**

```bash
sudo systemctl start isc-dhcp-server
```

**Terminal 2 (Secondary) - immédiatement après :**

```bash
sudo systemctl start isc-dhcp-server
```

---

**4. Observer les logs des DEUX serveurs :**

**Primary :**

```bash
sudo tail -f /var/log/syslog | grep dhcpd
```

**Résultat attendu :**

```
dhcpd[12345]: failover peer dhcp-failover: I move from startup to potential-conflict
dhcpd[12345]: failover peer dhcp-failover: peer moves from startup to potential-conflict
dhcpd[12345]: failover peer dhcp-failover: conflict resolution: 192.168.10.199 primary wins
dhcpd[12345]: failover peer dhcp-failover: I move from potential-conflict to normal
dhcpd[12345]: failover peer dhcp-failover: peer moves from potential-conflict to normal
```

**Explication :**

**POTENTIAL-CONFLICT**
- Les deux serveurs redémarrent en même temps
- Comparent leurs baux
- Détectent des différences

**Résolution de conflit**
- Pour chaque bail différent :
  - Le PRIMARY gagne (par défaut)
  - Ou le bail le plus récent gagne
- Le secondary adopte les baux du primary

**Transition vers NORMAL**
- Conflit résolu
- Les deux serveurs synchronisés

**[OK] Résolution de conflit automatique !**

---

#### Test 8 : Monitoring continu de l'état du failover

**Créer un script pour surveiller l'état du failover en continu.**

**Sur le PRIMARY (ou SECONDARY) :**

```bash
sudo nano /usr/local/bin/dhcp-failover-status.sh
```

**Contenu :**

```bash
#!/bin/bash
# ═══════════════════════════════════════════════════════════════
# Script de monitoring de l'état du failover DHCP
# ═══════════════════════════════════════════════════════════════

PEER_NAME="dhcp-failover"

# Fonction pour convertir l'état hexadécimal en texte
state_to_text() {
    case $1 in
        01) echo "STARTUP" ;;
        02) echo "NORMAL" ;;
        03) echo "COMMUNICATIONS-INTERRUPTED" ;;
        04) echo "PARTNER-DOWN" ;;
        05) echo "POTENTIAL-CONFLICT" ;;
        06) echo "RECOVER" ;;
        07) echo "PAUSED" ;;
        08) echo "SHUTDOWN" ;;
        09) echo "RESOLUTION-INTERRUPTED" ;;
        0a) echo "CONFLICT-DONE" ;;
        ff) echo "UNKNOWN/DOWN" ;;
        *) echo "UNKNOWN ($1)" ;;
    esac
}

# Interroger omshell
RESULT=$(cat <<EOF | omshell 2>/dev/null
server 127.0.0.1
connect
new failover-state
set name = "$PEER_NAME"
open
EOF
)

# Extraire l'état local et du partenaire
LOCAL_STATE=$(echo "$RESULT" | grep "local-state" | awk '{print $3}')
PARTNER_STATE=$(echo "$RESULT" | grep "partner-state" | awk '{print $3}')

# Convertir en texte
LOCAL_TEXT=$(state_to_text "$LOCAL_STATE")
PARTNER_TEXT=$(state_to_text "$PARTNER_STATE")

# Afficher le résultat
echo "═══════════════════════════════════════════════════════════"
echo "État du failover DHCP : $PEER_NAME"
echo "═══════════════════════════════════════════════════════════"
echo "Date : $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
echo "État local     : $LOCAL_TEXT ($LOCAL_STATE)"
echo "État partenaire: $PARTNER_TEXT ($PARTNER_STATE)"
echo ""

# Alertes
if [ "$LOCAL_STATE" == "02" ] && [ "$PARTNER_STATE" == "02" ]; then
    echo "[OK] Statut : NORMAL - Failover opérationnel"
elif [ "$LOCAL_STATE" == "04" ] || [ "$PARTNER_STATE" == "04" ]; then
    echo "[ATTENTION]  Statut : DÉGRADÉ - Un serveur est DOWN"
elif [ "$LOCAL_STATE" == "03" ] || [ "$PARTNER_STATE" == "03" ]; then
    echo "[ATTENTION]  Statut : COMMUNICATION INTERROMPUE"
else
    echo "ℹ  Statut : $LOCAL_TEXT / $PARTNER_TEXT"
fi

echo "═══════════════════════════════════════════════════════════"

# Statistiques supplémentaires
MCLT=$(echo "$RESULT" | grep "mclt" | awk '{print $3}')
LOAD_BALANCE=$(echo "$RESULT" | grep "load-balance-max-secs" | awk '{print $3}')

if [ ! -z "$MCLT" ]; then
    echo ""
    echo "Paramètres failover :"
    echo "  MCLT                   : $MCLT secondes"
    echo "  Load balance max secs  : $LOAD_BALANCE secondes"
fi
```

**Rendre exécutable :**

```bash
sudo chmod +x /usr/local/bin/dhcp-failover-status.sh
```

---

**Exécuter le script :**

```bash
sudo /usr/local/bin/dhcp-failover-status.sh
```

**Résultat (exemple en mode normal) :**

```
═══════════════════════════════════════════════════════════
État du failover DHCP : dhcp-failover
═══════════════════════════════════════════════════════════
Date : 2024-12-17 00:15:30

État local     : NORMAL (02)
État partenaire: NORMAL (02)

[OK] Statut : NORMAL - Failover opérationnel
═══════════════════════════════════════════════════════════

Paramètres failover :
  MCLT                   : 3600 secondes
  Load balance max secs  : 3 secondes
```

---

**Programmer l'exécution périodique :**

```bash
sudo crontab -e
```

**Ajouter :**

```cron
# Vérifier l'état du failover toutes les 5 minutes
*/5 * * * * /usr/local/bin/dhcp-failover-status.sh >> /var/log/dhcp-failover-status.log 2>&1
```

**Consulter l'historique :**

```bash
tail -50 /var/log/dhcp-failover-status.log
```

---

### [OK] TESTS DE VALIDATION

**Checklist complète pour valider le failover :**

**1. Configuration initiale**

- [ ] Deux serveurs DHCP installés et configurés
- [ ] Secret partagé identique sur les deux serveurs
- [ ] Clé TSIG DDNS identique (si DDNS activé)
- [ ] Port 847 autorisé dans le pare-feu (bidirectionnel)
- [ ] Réservations identiques sur les deux serveurs

---

**2. État normal (les deux serveurs actifs)**

- [ ] Les deux serveurs en état NORMAL (omshell)
- [ ] Logs montrent "I move from startup to normal"
- [ ] Load balancing fonctionne (~50/50 des attributions)
- [ ] Synchronisation d'état (BNDUPD/BNDACK dans les logs)
- [ ] Fichiers dhcpd.leases identiques sur les deux serveurs

---

**3. Panne du primary**

- [ ] Secondary détecte la panne (COMMUNICATIONS-INTERRUPTED)
- [ ] Après 60s, transition vers PARTNER-DOWN
- [ ] Secondary répond seul aux nouvelles requêtes
- [ ] Les clients existants peuvent renouveler leur bail
- [ ] Nouveaux clients obtiennent des IPs

---

**4. Retour du primary**

- [ ] Primary redémarre en état RECOVER
- [ ] Primary demande resynchronisation (UPDREQALL)
- [ ] Secondary envoie tous les baux (BNDUPD resync)
- [ ] Transition vers NORMAL sur les deux serveurs
- [ ] Load balancing reprend (50/50)

---

**5. Panne du secondary**

- [ ] Primary détecte la panne (COMMUNICATIONS-INTERRUPTED)
- [ ] Après 60s, transition vers PARTNER-DOWN
- [ ] Primary répond seul aux nouvelles requêtes
- [ ] Retour du secondary : resynchronisation identique

---

**6. Redémarrage simultané**

- [ ] Les deux serveurs en POTENTIAL-CONFLICT
- [ ] Résolution de conflit automatique
- [ ] Transition vers NORMAL
- [ ] Pas de perte de baux

---

**7. Intégration avec DDNS (si configuré)**

- [ ] Les deux serveurs mettent à jour le DNS
- [ ] Pas de doublons dans le DNS
- [ ] Enregistrements DNS cohérents

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "failover peer: bind update failed: address already bound"

**Message complet :**

```
dhcpd[12345]: failover peer dhcp-failover: bind update failed: address already bound
```

**Cause :**

Les deux serveurs ont attribué la même IP à deux clients différents.

**Causes possibles :**

**1. Les serveurs n'étaient pas synchronisés au moment de l'attribution**

**Solution :**

Forcer la resynchronisation :

```bash
# Arrêter les deux serveurs
sudo systemctl stop isc-dhcp-server

# Supprimer les fichiers de baux
sudo rm /var/lib/dhcp/dhcpd.leases
sudo touch /var/lib/dhcp/dhcpd.leases
sudo chown dhcpd:dhcpd /var/lib/dhcp/dhcpd.leases

# Redémarrer (primary en premier, puis secondary)
sudo systemctl start isc-dhcp-server
```

---

**2. Split mal configuré**

**Vérifier que `split` est défini SEULEMENT sur le primary.**

**Primary :**

```conf
failover peer "dhcp-failover" {
    primary;
    split 128;  # [OK] OK
    ...
}
```

**Secondary :**

```conf
failover peer "dhcp-failover" {
    secondary;
    # PAS de split ! [X]
    ...
}
```

---

#### Erreur 2 : "failover peer: refused to connect (invalid failover relationship)"

**Message complet :**

```
dhcpd[12345]: failover peer dhcp-failover: refused to connect (invalid failover relationship)
```

**Causes possibles :**

**1. Secret partagé différent**

**Vérifier les secrets :**

**Primary :**

```bash
grep "secret" /etc/dhcp/dhcpd.conf
```

**Secondary :**

```bash
grep "secret" /etc/dhcp/dhcpd.conf
```

**Les secrets DOIVENT être identiques.**

---

**2. Nom du peer différent**

**Primary :**

```conf
failover peer "dhcp-failover" {  # <- Nom
```

**Secondary :**

```conf
failover peer "dhcp-failover" {  # <- DOIT être le même
```

---

#### Erreur 3 : Les deux serveurs restent en COMMUNICATIONS-INTERRUPTED

**Symptôme :**

Aucun serveur ne passe en PARTNER-DOWN, ils restent bloqués en COMMUNICATIONS-INTERRUPTED.

**Causes possibles :**

**1. Port 847 bloqué par le pare-feu**

**Vérifier la connectivité :**

**Sur le PRIMARY :**

```bash
telnet 192.168.10.11 847
```

**Si échec :**

```
Trying 192.168.10.11...
telnet: Unable to connect to remote host: Connection refused
```

**Solution :**

```bash
# Sur le secondary
sudo ufw allow from 192.168.10.10 to any port 847 proto tcp
```

---

**2. Serveurs sur des sous-réseaux différents (problème de routage)**

**Vérifier :**

```bash
ping 192.168.10.11  # Depuis le primary
ping 192.168.10.10  # Depuis le secondary
```

**Si échec, vérifier la configuration réseau.**

---

#### Erreur 4 : "mclt: not specified" sur le secondary

**Message complet :**

```
dhcpd[23456]: failover peer dhcp-failover: mclt: not specified
```

**Cause :**

MCLT n'est pas défini sur le primary.

**Solution :**

**Sur le PRIMARY, ajouter dans le bloc failover :**

```conf
failover peer "dhcp-failover" {
    primary;
    ...
    max-client-lead-time 3600;  # <- Ajouter cette ligne
    ...
}
```

**Redémarrer les deux serveurs.**

---

#### Erreur 5 : Fichiers dhcpd.leases désynchronisés

**Symptôme :**

Les deux serveurs ont des listes de baux différentes.

**Solution :**

**Forcer la resynchronisation :**

**1. Arrêter le secondary :**

```bash
sudo systemctl stop isc-dhcp-server
```

---

**2. Supprimer les baux du secondary :**

```bash
sudo rm /var/lib/dhcp/dhcpd.leases
sudo touch /var/lib/dhcp/dhcpd.leases
sudo chown dhcpd:dhcpd /var/lib/dhcp/dhcpd.leases
```

---

**3. Redémarrer le secondary :**

```bash
sudo systemctl start isc-dhcp-server
```

**Le secondary se synchronisera automatiquement avec le primary (état RECOVER).**

---

#### Erreur 6 : "binding state free pool exhausted"

**Message complet :**

```
dhcpd[12345]: DHCPDISCOVER from 00:0c:29:ab:cd:ef via ens33: network 192.168.10.0/24: no free leases
```

**Cause :**

La plage d'adresses est épuisée.

**Solutions :**

**1. Vérifier le nombre de baux actifs :**

```bash
grep "binding state active" /var/lib/dhcp/dhcpd.leases | wc -l
```

**Si >= 101 (taille de la plage 100-200) -> Plage saturée.**

---

**2. Augmenter la plage :**

```conf
pool {
    failover peer "dhcp-failover";
    range 192.168.10.100 192.168.10.220;  # <- Étendu à 121 adresses
}
```

**Redémarrer les deux serveurs.**

---

**3. Réduire la durée des baux :**

```conf
default-lease-time 43200;  # 12 heures au lieu de 24h
```

**Les baux expirent plus vite, libérant des adresses.**

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Architecture failover DHCP**
- Deux serveurs en tandem (primary + secondary)
- Communication via port TCP 847
- Synchronisation d'état en temps réel

**2. Modes de failover**
- Load balance : Les deux serveurs actifs (50/50)
- Hot standby : Primary actif, secondary passif
- Recommandé : load balance (meilleure utilisation des ressources)

**3. États du failover**
- NORMAL : Fonctionnement normal
- COMMUNICATIONS-INTERRUPTED : Perte de communication
- PARTNER-DOWN : Un serveur down, l'autre prend le relais
- RECOVER : Resynchronisation après panne
- POTENTIAL-CONFLICT : Résolution de conflit au redémarrage

**4. Paramètres critiques**
- `mclt` (MCLT) : Durée max sans confirmation (ex: 3600s)
- `split` : Répartition de charge (128 = 50/50)
- `max-response-delay` : Délai avant PARTNER-DOWN (ex: 60s)
- `load balance max seconds` : Délai de réponse backup (ex: 3s)

**5. Synchronisation d'état**
- Messages BNDUPD/BNDACK pour chaque bail
- POOLREQ/POOLRESP pour les plages
- UPDREQ pour la resynchronisation complète

**6. Sécurité**
- Secret partagé pour authentifier les serveurs
- Empêche un serveur malveillant de rejoindre le failover
- TSIG pour DDNS (si activé)

**7. Bonnes pratiques**
- Démarrer le primary avant le secondary
- Surveiller l'état avec omshell ou scripts
- Tester régulièrement les scénarios de panne
- Documenter les procédures de récupération
- Sauvegarder les fichiers dhcpd.conf et dhcpd.leases

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Monitoring avancé avec Nagios/Zabbix**

**Créer un check Nagios pour surveiller l'état du failover :**

```bash
#!/bin/bash
# /usr/lib/nagios/plugins/check_dhcp_failover.sh

PEER_NAME="dhcp-failover"
RESULT=$(cat <<EOF | omshell 2>/dev/null
server 127.0.0.1
connect
new failover-state
set name = "$PEER_NAME"
open
EOF
)

LOCAL_STATE=$(echo "$RESULT" | grep "local-state" | awk '{print $3}')
PARTNER_STATE=$(echo "$RESULT" | grep "partner-state" | awk '{print $3}')

if [ "$LOCAL_STATE" == "02" ] && [ "$PARTNER_STATE" == "02" ]; then
    echo "OK - Failover NORMAL"
    exit 0
elif [ "$LOCAL_STATE" == "04" ] || [ "$PARTNER_STATE" == "04" ]; then
    echo "WARNING - Failover PARTNER-DOWN"
    exit 1
elif [ "$LOCAL_STATE" == "03" ] || [ "$PARTNER_STATE" == "03" ]; then
    echo "WARNING - Failover COMMUNICATIONS-INTERRUPTED"
    exit 1
else
    echo "CRITICAL - Failover état inconnu: $LOCAL_STATE / $PARTNER_STATE"
    exit 2
fi
```

---

**2. Basculement manuel forcé**

**Forcer un serveur en mode PARTNER-DOWN (si l'autre est définitivement mort) :**

```bash
omshell
> server 127.0.0.1
> connect
> new failover-state
> set name = "dhcp-failover"
> open
> set local-state = 4
> update
```

**[ATTENTION] À utiliser seulement si le partenaire est VRAIMENT mort et ne reviendra PAS !**

---

**3. Geo-redondance (serveurs dans des datacenters différents)**

**Pour une haute disponibilité géographique :**

```
Datacenter A (Paris)          Datacenter B (Lyon)
├─ DHCP Primary               ├─ DHCP Secondary
│  192.168.10.10              │  192.168.10.11
│                             │
└─ VPN IPsec <-──────────────-> └─ VPN IPsec
```

**Interconnexion via VPN pour sécuriser la communication failover.**

---

**4. Split-scope vs Failover**

**Comparaison :**

| Critère | Failover | Split-scope |
|---------|----------|-------------|
| Synchronisation | [OK] Automatique | [X] Aucune |
| Basculement | [OK] Automatique | [X] Manuel |
| Configuration | Complexe | Simple |
| Risque de conflit | Faible | Élevé |

**Split-scope (ancienne méthode) :**
```
Primary   : range 192.168.10.100-150
Secondary : range 192.168.10.151-200

Problème :
Si primary down, les clients de sa plage (100-150)
ne peuvent PAS renouveler leur bail
```

**Failover (moderne) :**
```
Les deux partagent 192.168.10.100-200
En cas de panne, le survivant utilise TOUTE la plage
```

**-> TOUJOURS préférer le failover !**

---

**5. Intégration avec Ansible pour automatisation**

**Playbook Ansible pour déployer le failover :**

```yaml
---
- name: Déployer DHCP Failover
  hosts: dhcp_servers
  become: yes
  
  vars:
    failover_secret: "k8mP2qR5tU7vW9xY1zB3cD4eF6gH8iJ0kL2mN4oP6qR8="
    primary_ip: "192.168.10.10"
    secondary_ip: "192.168.10.11"
  
  tasks:
    - name: Installer ISC DHCP Server
      apt:
        name: isc-dhcp-server
        state: present
    
    - name: Copier la configuration DHCP
      template:
        src: dhcpd.conf.j2
        dest: /etc/dhcp/dhcpd.conf
      notify: restart dhcp
    
    - name: Autoriser port 847
      ufw:
        rule: allow
        port: 847
        proto: tcp
        from_ip: "{{ hostvars[item]['ansible_default_ipv4']['address'] }}"
      loop: "{{ groups['dhcp_servers'] }}"
  
  handlers:
    - name: restart dhcp
      service:
        name: isc-dhcp-server
        state: restarted
```

---

## [COURS] CONCLUSION DE L'EXERCICE 4

**[OK] Félicitations ! Tu maîtrises maintenant la haute disponibilité DHCP !**

**Ce que tu as appris :**
- Comprendre les concepts de haute disponibilité (HA)
- Configurer le failover DHCP avec ISC DHCP Server
- Mettre en place la communication peer-to-peer entre serveurs
- Générer et utiliser un secret partagé pour l'authentification
- Configurer le load balancing DHCP (répartition 50/50)
- Synchroniser l'état des baux entre serveurs
- Gérer les transitions d'état (NORMAL, PARTNER-DOWN, RECOVER)
- Tester la résilience en cas de panne
- Monitorer l'état du failover avec omshell
- Résoudre les problèmes courants de failover

**Compétences acquises :**
- [OK] Haute disponibilité DHCP (niveau avancé)
- [OK] Failover et load balancing
- [OK] Synchronisation d'état en temps réel
- [OK] Gestion de pannes et récupération
- [OK] Monitoring d'infrastructure critique
- [OK] Architecture résiliente

**Architecture finale :**
```
2 serveurs DHCP actifs simultanément
v
Load balancing 50/50
v
Synchronisation d'état continue
v
Panne d'un serveur -> Basculement automatique
v
Zéro interruption de service
v
Haute disponibilité [OK]
```

**Temps moyen de réalisation :** 3-4 heures

**Prochaine étape :** Exercice 5 - Sécurisation et monitoring avancé ! [VERROUILLE]

---

Je continue avec l'exercice 5 (sécurisation et monitoring avancé) ?

# [ROUGE] EXERCICE 5 : SÉCURISATION ET MONITORING AVANCÉ DHCP

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu as mis en place une infrastructure DHCP robuste et hautement disponible (exercices 1-4). Le responsable IT est impressionné, mais soulève de nouvelles préoccupations liées à la sécurité et au monitoring :

**Problématiques de sécurité identifiées :**

1. **Rogue DHCP Servers** : Un employé malveillant ou une erreur de configuration pourrait introduire un serveur DHCP non autorisé sur le réseau
2. **DHCP Starvation** : Une attaque DoS pourrait épuiser toutes les adresses IP disponibles
3. **IP Spoofing** : Des clients pourraient usurper des adresses MAC pour obtenir des réservations privilégiées
4. **Manque de visibilité** : Impossible de savoir en temps réel l'état du service (taux d'utilisation, performance, anomalies)
5. **Conformité réglementaire** : Besoin d'auditer qui obtient quelle IP, quand, et pourquoi

**Mission :** Sécuriser et monitorer l'infrastructure DHCP pour :
1. Détecter et bloquer les serveurs DHCP non autorisés
2. Protéger contre les attaques DoS (starvation)
3. Auditer toutes les attributions d'IP (traçabilité complète)
4. Monitorer les métriques en temps réel (dashboards)
5. Alerter en cas d'anomalie (pool épuisé, serveur down, etc.)
6. Générer des rapports de conformité

### Cahier des charges

**Architecture de sécurité :**
```
┌─────────────────────────────────────────────────────────────┐
│                  COUCHE DE SÉCURITÉ                         │
├─────────────────────────────────────────────────────────────┤
│ 1. DHCP Snooping (switches)                                 │
│    └─ Trust ports : Serveurs DHCP autorisés                 │
│    └─ Untrust ports : Tous les autres ports                 │
│                                                              │
│ 2. Port Security (MAC limiting)                             │
│    └─ Max 2 MAC par port                                    │
│                                                              │
│ 3. Rate Limiting                                            │
│    └─ Max 10 DHCP requêtes/seconde par port                 │
│                                                              │
│ 4. Authentification DHCP (option 82)                        │
│    └─ Circuit-ID pour identifier le port switch             │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                  COUCHE DE MONITORING                       │
├─────────────────────────────────────────────────────────────┤
│ 1. Métriques temps réel                                     │
│    ├─ Taux d'utilisation des pools                          │
│    ├─ Nombre d'attributions/seconde                         │
│    ├─ Latence de réponse DHCP                               │
│    └─ État du failover                                      │
│                                                              │
│ 2. Logging centralisé                                       │
│    ├─ Syslog vers serveur central                           │
│    ├─ Enrichissement avec GeoIP                             │
│    └─ Corrélation d'événements                              │
│                                                              │
│ 3. Dashboards Grafana                                       │
│    ├─ Utilisation pools par département                     │
│    ├─ Top 10 clients                                        │
│    └─ Historique des pannes                                 │
│                                                              │
│ 4. Alerting                                                 │
│    ├─ Email : Pool > 80%                                    │
│    ├─ SMS : Serveur failover down                           │
│    └─ Slack : Rogue DHCP détecté                            │
└─────────────────────────────────────────────────────────────┘
```

**Outils à déployer :**

**Sécurité :**
- DHCP Snooping (configuration switch Cisco/HP)
- fail2ban pour bloquer les attaques
- Tripwire pour détecter modifications non autorisées
- Audit logging avec auditd

**Monitoring :**
- Prometheus (collecte métriques)
- Grafana (dashboards)
- Loki (logs)
- Alertmanager (alertes)
- dhcp-stats (statistiques custom)

### Contraintes techniques

- Infrastructure DHCP failover existante (exercice 4)
- Conformité RGPD (anonymisation des données)
- Rétention logs : 1 an minimum
- RTO (Recovery Time Objective) : 5 minutes maximum
- SLA : 99.9% de disponibilité
- Temps estimé : 4-5 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Configurer DHCP Snooping sur les switches
- [OK] Détecter et bloquer les rogue DHCP servers
- [OK] Protéger contre les attaques DHCP starvation
- [OK] Activer l'option 82 (Relay Agent Information)
- [OK] Mettre en place un logging centralisé avec syslog-ng
- [OK] Déployer Prometheus et node_exporter
- [OK] Créer un exporter custom pour les métriques DHCP
- [OK] Configurer Grafana avec dashboards DHCP
- [OK] Mettre en place Alertmanager pour les notifications
- [OK] Auditer les attributions IP avec auditd
- [OK] Générer des rapports de conformité
- [OK] Optimiser les performances DHCP

---

## [DOCS] PRÉREQUIS

- Exercices 1, 2, 3 et 4 terminés avec succès
- Infrastructure DHCP failover opérationnelle
- Accès à un switch manageable (ou simulation)
- Serveur dédié pour le monitoring (ou VM)
- Connaissances réseau de base

---

## [IDEE] COMPRENDRE LA SÉCURITÉ DHCP

### Les menaces DHCP

#### 1. Rogue DHCP Server (Serveur DHCP malveillant)

**Scénario d'attaque :**

```
Réseau normal :
Client -> DHCPDISCOVER (broadcast)
         v
Serveur DHCP légitime -> DHCPOFFER (IP: 192.168.10.105, Gateway: .1, DNS: .10)
         v
Client accepte [OK]

Avec Rogue DHCP :
Client -> DHCPDISCOVER (broadcast)
         v
         ├-> Serveur légitime -> DHCPOFFER (IP: 192.168.10.105, Gateway: .1)
         └-> Rogue DHCP -> DHCPOFFER (IP: 192.168.10.200, Gateway: 192.168.10.99)
         
Le client accepte la PREMIÈRE réponse reçue !

Si Rogue répond plus vite :
Client utilise Gateway: 192.168.10.99 (attaquant)
         v
TOUT le trafic passe par l'attaquant
         v
Man-in-the-Middle [X]
```

**Conséquences :**

**1. Man-in-the-Middle (MitM)**
- L'attaquant voit tout le trafic réseau
- Vol de mots de passe, données sensibles
- Injection de malware

**2. DNS Spoofing**
- Rogue DHCP fournit un DNS malveillant
- Redirection vers des sites de phishing
- Exemple : google.com -> 192.168.10.99 (site malveillant)

**3. Déni de service**
- Rogue DHCP fournit une passerelle inexistante
- Les clients n'ont plus accès à Internet

**4. Évasion de sécurité**
- Bypass des firewalls et proxies
- Tunnel vers l'extérieur via l'attaquant

---

**Comment ça arrive ?**

**Cas 1 : Employé malveillant**
```
Un développeur branche un Raspberry Pi
Configure dnsmasq ou isc-dhcp-server
Active le serveur DHCP
-> Rogue DHCP opérationnel
```

**Cas 2 : Erreur de configuration**
```
Un employé branche son routeur personnel (WiFi)
Le routeur a DHCP activé par défaut
-> Rogue DHCP accidentel
```

**Cas 3 : Équipement compromis**
```
Un PC infecté par un malware
Le malware active un serveur DHCP
-> Propagation automatique
```

---

#### 2. DHCP Starvation (Épuisement des adresses)

**Scénario d'attaque :**

```python
# Script d'attaque DHCP starvation
from scapy.all import *

def dhcp_starvation():
    for i in range(1000):
        # Générer une MAC aléatoire
        fake_mac = RandMAC()
        
        # Envoyer DHCPDISCOVER
        dhcp_discover = (
            Ether(dst="ff:ff:ff:ff:ff:ff", src=fake_mac) /
            IP(src="0.0.0.0", dst="255.255.255.255") /
            UDP(sport=68, dport=67) /
            BOOTP(chaddr=fake_mac) /
            DHCP(options=[("message-type", "discover"), "end"])
        )
        sendp(dhcp_discover, iface="eth0", verbose=0)
        
        print(f"[+] Requête {i+1} envoyée avec MAC: {fake_mac}")

dhcp_starvation()
```

**Résultat :**

```
Serveur DHCP reçoit 1000 DHCPDISCOVER
-> Attribue 1000 adresses IP
-> Pool épuisé en quelques secondes
-> Plus d'adresses disponibles pour les clients légitimes
-> Déni de service [X]
```

**Conséquences :**

- Nouveaux employés ne peuvent pas se connecter
- Machines existantes ne peuvent pas renouveler leur bail
- Interruption du service réseau
- Impact business critique

---

#### 3. MAC Spoofing (Usurpation d'adresse MAC)

**Scénario d'attaque :**

```
Réservation DHCP pour le serveur web :
MAC: 00:11:22:33:44:55 -> IP: 192.168.10.7 (prioritaire, VLAN spécial)

Attaquant modifie sa MAC :
sudo ifconfig eth0 hw ether 00:11:22:33:44:55

Attaquant demande une IP :
sudo dhclient eth0

Serveur DHCP voit MAC: 00:11:22:33:44:55
-> Pense que c'est le serveur web
-> Attribue 192.168.10.7 + privilèges
-> Attaquant obtient accès privilégié [X]
```

**Conséquences :**

- Élévation de privilèges
- Accès à des VLANs restreints
- Bypass des ACLs (Access Control Lists)

---

### Solutions de sécurité

#### 1. DHCP Snooping

**DHCP Snooping** = Fonctionnalité du switch pour filtrer les messages DHCP

**Principe :**

```
Switch avec DHCP Snooping activé :

Port 1-20 (Untrusted) :
├─ Messages DHCP SERVER bloqués (DHCPOFFER, DHCPACK)
├─ Seulement messages DHCP CLIENT autorisés (DHCPDISCOVER, DHCPREQUEST)
└─ Rogue DHCP ne peut PAS répondre

Port 24 (Trusted) :
├─ TOUS les messages DHCP autorisés
└─ Serveur DHCP légitime connecté ici
```

**Configuration switch Cisco (exemple) :**

```cisco
! Activer DHCP Snooping globalement
ip dhcp snooping

! Activer sur VLAN 10
ip dhcp snooping vlan 10

! Port trusté (serveur DHCP)
interface GigabitEthernet0/24
 description "Serveur DHCP Primaire"
 ip dhcp snooping trust

! Ports non trustés (clients)
interface range GigabitEthernet0/1-20
 description "Postes utilisateurs"
 ip dhcp snooping limit rate 10
```

**Résultat :**

- Rogue DHCP ne peut PAS envoyer de DHCPOFFER
- Seul le serveur sur le port trusté peut répondre
- Protection automatique [OK]

---

#### 2. Option 82 (Relay Agent Information)

**Option DHCP 82** = Information insérée par le switch/relay agent

**Contenu de l'option 82 :**

**Circuit-ID** (sous-option 1) : Identifie le port du switch
- Exemple : "Gi0/12" (GigabitEthernet 0/12)

**Remote-ID** (sous-option 2) : Identifie le switch lui-même
- Exemple : MAC du switch ou hostname

**Utilité :**

```
Client sur port Gi0/12 du switch SW-PARIS-01
-> DHCPDISCOVER
-> Switch ajoute option 82 :
   Circuit-ID: Gi0/12
   Remote-ID: SW-PARIS-01
-> Serveur DHCP reçoit la requête
-> Serveur DHCP sait : "Ce client est sur SW-PARIS-01, port Gi0/12"
-> Peut attribuer une IP spécifique selon la localisation
```

**Applications :**

**1. Attribution par localisation**
```conf
# dhcpd.conf
class "bureau-rh" {
    match if option agent.circuit-id = "Gi0/12";
}

pool {
    allow members of "bureau-rh";
    range 192.168.10.50 192.168.10.70;
}
```

**2. Audit et traçabilité**
```
Logs DHCP :
"192.168.10.105 attribuée à MAC 00:0c:29:ab:cd:ef depuis SW-PARIS-01 port Gi0/12"
-> Géolocalisation précise du client
```

**3. Sécurité renforcée**
```
Bloquer les clients sans option 82 :
-> Seulement les clients connectés via switch autorisé
-> Pas de connexion directe au serveur DHCP
```

---

#### 3. Rate Limiting

**Rate Limiting** = Limiter le nombre de requêtes DHCP par seconde

**Protection contre DHCP Starvation :**

```cisco
! Cisco switch
interface GigabitEthernet0/5
 ip dhcp snooping limit rate 10
```

**Signification :**
- Maximum 10 paquets DHCP par seconde sur ce port
- Au-delà, le switch DROP les paquets
- Bloque les attaques automatisées

**Scénario :**

```
Attaque DHCP Starvation :
Script envoie 1000 requêtes/seconde
-> Switch détecte > 10 requêtes/seconde
-> Switch bloque le port (err-disabled)
-> Attaque neutralisée [OK]
```

---

#### 4. Port Security

**Port Security** = Limiter le nombre de MACs par port

**Configuration Cisco :**

```cisco
interface GigabitEthernet0/5
 switchport mode access
 switchport port-security
 switchport port-security maximum 2
 switchport port-security violation restrict
 switchport port-security mac-address sticky
```

**Explication :**

**`switchport port-security maximum 2`**
- Maximum 2 adresses MAC différentes sur ce port
- Bloque le MAC spoofing à grande échelle

**`switchport port-security violation restrict`**
- Si tentative d'utiliser une 3ème MAC :
  - DROP les paquets
  - Génère une alerte SNMP
  - N'erre-disable PAS le port (continue pour les MACs autorisées)

**`switchport port-security mac-address sticky`**
- Apprend automatiquement les MACs
- Les enregistre dans la config
- Persiste après reboot

**Actions possibles en cas de violation :**

**protect** : DROP silencieux (pas d'alerte)
**restrict** : DROP + alerte SNMP + log
**shutdown** : err-disable le port (nécessite intervention manuelle)

---

## [OK] SOLUTION COMPLÈTE

### PARTIE 1 : SÉCURISATION DHCP

#### ÉTAPE 1 : Configurer DHCP Snooping (sur switch Cisco)

**[ATTENTION] Cette étape nécessite un switch manageable Cisco/HP/etc.**

**Si tu n'as pas de switch physique, passe à l'ÉTAPE 2 (simulation logicielle).**

---

**Sur un switch Cisco IOS :**

**1. Se connecter au switch :**

```bash
ssh admin@192.168.10.254
# ou
telnet 192.168.10.254
```

---

**2. Passer en mode privilégié :**

```cisco
Switch> enable
Switch#
```

---

**3. Passer en mode configuration :**

```cisco
Switch# configure terminal
Switch(config)#
```

---

**4. Activer DHCP Snooping globalement :**

```cisco
Switch(config)# ip dhcp snooping
```

**Explication :**
- Active la fonctionnalité DHCP Snooping sur le switch
- Par défaut, TOUS les ports sont "untrusted"

---

**5. Activer DHCP Snooping sur les VLANs :**

```cisco
Switch(config)# ip dhcp snooping vlan 1,10,20
```

**Explication :**
- Active DHCP Snooping sur VLAN 1, 10 et 20
- Ajuste selon tes VLANs

---

**6. Configurer l'option 82 (Relay Agent Information) :**

```cisco
Switch(config)# ip dhcp snooping information option
```

**Explication :**
- Le switch insère l'option 82 dans les requêtes DHCP
- Contient : Circuit-ID (port) et Remote-ID (switch)

---

**7. Configurer les ports trustés (serveurs DHCP) :**

```cisco
Switch(config)# interface GigabitEthernet0/24
Switch(config-if)# description "DHCP Primary - 192.168.10.10"
Switch(config-if)# ip dhcp snooping trust
Switch(config-if)# exit

Switch(config)# interface GigabitEthernet0/23
Switch(config-if)# description "DHCP Secondary - 192.168.10.11"
Switch(config-if)# ip dhcp snooping trust
Switch(config-if)# exit
```

**Explication :**
- Ports 23 et 24 = trustés
- Peuvent envoyer DHCPOFFER et DHCPACK
- Connecter les serveurs DHCP sur ces ports

---

**8. Configurer le rate limiting sur les ports clients :**

```cisco
Switch(config)# interface range GigabitEthernet0/1-22
Switch(config-if-range)# description "Ports clients"
Switch(config-if-range)# ip dhcp snooping limit rate 10
Switch(config-if-range)# exit
```

**Explication :**
- Ports 1-22 = clients
- Maximum 10 paquets DHCP par seconde
- Bloque les attaques de starvation

---

**9. Activer Port Security (optionnel mais recommandé) :**

```cisco
Switch(config)# interface range GigabitEthernet0/1-22
Switch(config-if-range)# switchport mode access
Switch(config-if-range)# switchport port-security
Switch(config-if-range)# switchport port-security maximum 2
Switch(config-if-range)# switchport port-security violation restrict
Switch(config-if-range)# switchport port-security mac-address sticky
Switch(config-if-range)# switchport port-security aging time 5
Switch(config-if-range)# switchport port-security aging type inactivity
Switch(config-if-range)# exit
```

**Explication :**

**`switchport port-security maximum 2`**
- Max 2 MACs par port (PC + téléphone IP par exemple)

**`switchport port-security violation restrict`**
- En cas de violation : DROP + alerte

**`switchport port-security mac-address sticky`**
- Apprend et enregistre automatiquement les MACs

**`switchport port-security aging time 5`**
- Les MACs expirent après 5 minutes d'inactivité

---

**10. Sauvegarder la configuration :**

```cisco
Switch(config)# exit
Switch# write memory
```

**Ou :**

```cisco
Switch# copy running-config startup-config
```

---

**11. Vérifier la configuration :**

```cisco
Switch# show ip dhcp snooping
```

**Résultat attendu :**

```
Switch DHCP snooping is enabled
DHCP snooping is configured on following VLANs:
1,10,20
DHCP snooping is operational on following VLANs:
1,10,20
Insertion of option 82 is enabled
Interface                  Trusted    Rate limit (pps)
-----------------------    -------    ----------------
GigabitEthernet0/24        yes        unlimited
GigabitEthernet0/23        yes        unlimited
GigabitEthernet0/1         no         10
GigabitEthernet0/2         no         10
[...]
```

**[OK] DHCP Snooping activé !**

---

**12. Vérifier la binding table (table des baux) :**

```cisco
Switch# show ip dhcp snooping binding
```

**Résultat (après que des clients ont obtenu des IPs) :**

```
MacAddress          IpAddress    Lease(sec)  Type           VLAN  Interface
------------------  -----------  ----------  -------------  ----  --------------------
00:0C:29:AB:CD:EF   192.168.10.105  86395     dhcp-snooping   1    GigabitEthernet0/5
00:0C:29:12:34:56   192.168.10.106  86392     dhcp-snooping   1    GigabitEthernet0/7
```

**Informations :**
- Le switch a appris les bindings MAC <-> IP
- Utilisé pour d'autres fonctionnalités (DAI, IP Source Guard)

---

#### ÉTAPE 2 : Simuler DHCP Snooping avec iptables (sans switch)

**Si tu n'as pas de switch manageable, on peut simuler certaines protections avec iptables sur le serveur DHCP.**

**Sur le serveur DHCP PRIMAIRE (192.168.10.10) :**

**1. Bloquer les réponses DHCP venant d'autres serveurs :**

```bash
# Bloquer les DHCPOFFER venant d'IPs autres que les serveurs autorisés
sudo iptables -A INPUT -p udp --sport 67 ! -s 192.168.10.10 -j DROP
sudo iptables -A INPUT -p udp --sport 67 ! -s 192.168.10.11 -j DROP
```

**Explication :**
- Bloque les paquets UDP venant du port 67 (serveur DHCP)
- Sauf s'ils viennent de .10 ou .11 (nos serveurs)
- Empêche un rogue DHCP de communiquer avec nos serveurs

---

**2. Rate limiting pour prévenir DHCP Starvation :**

```bash
# Limiter à 100 nouvelles connexions DHCP par minute
sudo iptables -A INPUT -p udp --dport 67 -m state --state NEW -m recent --set --name dhcp_limit
sudo iptables -A INPUT -p udp --dport 67 -m state --state NEW -m recent --update --seconds 60 --hitcount 100 --name dhcp_limit -j DROP
```

**Explication :**

**`-m recent --set --name dhcp_limit`**
- Enregistre l'IP source dans une liste "dhcp_limit"

**`-m recent --update --seconds 60 --hitcount 100`**
- Si la même IP a fait plus de 100 requêtes en 60 secondes
- DROP la requête

**Protection :**
- Un attaquant ne peut pas envoyer > 100 DHCPDISCOVER par minute
- Ralentit énormément les attaques de starvation

---

**3. Sauvegarder les règles iptables :**

```bash
sudo iptables-save | sudo tee /etc/iptables/rules.v4
```

**Installer iptables-persistent pour charger au boot :**

```bash
sudo apt install iptables-persistent -y
```

---

#### ÉTAPE 3 : Détecter les Rogue DHCP Servers

**Créer un script de détection de rogue DHCP servers.**

**Sur un serveur de monitoring (ou le serveur DHCP) :**

**1. Installer les dépendances :**

```bash
sudo apt install python3 python3-pip tcpdump -y
sudo pip3 install scapy
```

---

**2. Créer le script de détection :**

```bash
sudo nano /usr/local/bin/detect-rogue-dhcp.py
```

**Contenu :**

```python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Détection de Rogue DHCP Servers
Écoute le réseau et détecte les serveurs DHCP non autorisés
"""

from scapy.all import *
import datetime
import json
import os

# Configuration
AUTHORIZED_DHCP_SERVERS = [
    "192.168.10.10",  # Primary
    "192.168.10.11"   # Secondary
]

LOG_FILE = "/var/log/rogue-dhcp-detection.log"
ALERT_FILE = "/var/log/rogue-dhcp-alerts.json"

def log_message(message, level="INFO"):
    """
    Enregistre un message dans le fichier de log
    """
    timestamp = datetime.datetime.now().isoformat()
    log_entry = f"[{timestamp}] [{level}] {message}\n"
    
    print(log_entry.strip())
    
    with open(LOG_FILE, "a") as f:
        f.write(log_entry)

def send_alert(rogue_ip, rogue_mac):
    """
    Enregistre une alerte de rogue DHCP
    """
    alert = {
        "timestamp": datetime.datetime.now().isoformat(),
        "type": "ROGUE_DHCP_DETECTED",
        "rogue_ip": rogue_ip,
        "rogue_mac": rogue_mac,
        "severity": "CRITICAL"
    }
    
    # Enregistrer en JSON
    with open(ALERT_FILE, "a") as f:
        f.write(json.dumps(alert) + "\n")
    
    # Log
    log_message(
        f"[ALERTE] ROGUE DHCP DÉTECTÉ ! IP: {rogue_ip}, MAC: {rogue_mac}",
        level="CRITICAL"
    )
    
    # Envoyer une notification (à implémenter selon tes besoins)
    # Exemples : email, SMS, Slack, etc.
    # send_email_alert(alert)
    # send_slack_alert(alert)

def packet_handler(packet):
    """
    Traite chaque paquet DHCP reçu
    """
    # Vérifier si c'est un paquet DHCP
    if DHCP in packet:
        # Extraire le type de message DHCP
        dhcp_message_type = None
        for option in packet[DHCP].options:
            if isinstance(option, tuple) and option[0] == "message-type":
                dhcp_message_type = option[1]
                break
        
        # On s'intéresse aux DHCPOFFER et DHCPACK (réponses serveur)
        if dhcp_message_type in [2, 5]:  # 2=OFFER, 5=ACK
            # Extraire l'IP source (serveur DHCP)
            dhcp_server_ip = packet[IP].src
            dhcp_server_mac = packet[Ether].src
            
            # Vérifier si c'est un serveur autorisé
            if dhcp_server_ip not in AUTHORIZED_DHCP_SERVERS:
                # ROGUE DHCP DÉTECTÉ !
                send_alert(dhcp_server_ip, dhcp_server_mac)
            else:
                # Serveur légitime
                log_message(
                    f"[OK] DHCP légitime détecté : {dhcp_server_ip}",
                    level="DEBUG"
                )

def main():
    """
    Point d'entrée principal
    """
    log_message("[RECHERCHE] Démarrage de la détection de Rogue DHCP Servers")
    log_message(f"Serveurs autorisés : {', '.join(AUTHORIZED_DHCP_SERVERS)}")
    
    # Écouter le réseau
    # Filtre BPF : udp port 67 or 68 (DHCP)
    log_message("[EAR] Écoute du trafic DHCP en cours...")
    
    sniff(
        filter="udp port 67 or udp port 68",
        prn=packet_handler,
        store=0  # Ne pas stocker les paquets en mémoire
    )

if __name__ == "__main__":
    # Vérifier les permissions root
    if os.geteuid() != 0:
        print("[X] Ce script doit être exécuté en tant que root (sudo)")
        exit(1)
    
    try:
        main()
    except KeyboardInterrupt:
        log_message("\n[WAVING_HAND_SIGN] Arrêt de la détection")
    except Exception as e:
        log_message(f"[X] Erreur : {e}", level="ERROR")
```

**Rendre exécutable :**

```bash
sudo chmod +x /usr/local/bin/detect-rogue-dhcp.py
```

---

**3. Tester le script :**

```bash
sudo /usr/local/bin/detect-rogue-dhcp.py
```

**Résultat :**

```
[2024-12-17T01:00:00] [INFO] [RECHERCHE] Démarrage de la détection de Rogue DHCP Servers
[2024-12-17T01:00:00] [INFO] Serveurs autorisés : 192.168.10.10, 192.168.10.11
[2024-12-17T01:00:00] [INFO] [EAR] Écoute du trafic DHCP en cours...
```

**Le script est maintenant en écoute. Il détectera tout serveur DHCP non autorisé.**

---

**4. Créer un service systemd pour exécution permanente :**

```bash
sudo nano /etc/systemd/system/rogue-dhcp-detector.service
```

**Contenu :**

```ini
[Unit]
Description=Rogue DHCP Server Detector
After=network.target

[Service]
Type=simple
User=root
ExecStart=/usr/bin/python3 /usr/local/bin/detect-rogue-dhcp.py
Restart=always
RestartSec=10

# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=rogue-dhcp-detector

[Install]
WantedBy=multi-user.target
```

**Activer et démarrer le service :**

```bash
sudo systemctl daemon-reload
sudo systemctl enable rogue-dhcp-detector
sudo systemctl start rogue-dhcp-detector
```

**Vérifier l'état :**

```bash
sudo systemctl status rogue-dhcp-detector
```

**Résultat attendu :**

```
[BLACK_CIRCLE] rogue-dhcp-detector.service - Rogue DHCP Server Detector
     Loaded: loaded (/etc/systemd/system/rogue-dhcp-detector.service; enabled)
     Active: active (running) since Mon 2024-12-17 01:05:00 UTC; 10s ago
```

**[OK] Détection de rogue DHCP opérationnelle !**

---

**5. Tester la détection :**

**Sur une machine tierce, lancer un serveur DHCP (pour simuler un rogue) :**

```bash
# NE PAS FAIRE EN PRODUCTION !
sudo dnsmasq --interface=eth0 --dhcp-range=192.168.10.220,192.168.10.230,12h
```

**Sur le serveur de monitoring, vérifier les logs :**

```bash
sudo tail -f /var/log/rogue-dhcp-detection.log
```

**Résultat attendu :**

```
[2024-12-17T01:10:00] [CRITICAL] [ALERTE] ROGUE DHCP DÉTECTÉ ! IP: 192.168.10.50, MAC: 00:0c:29:aa:bb:cc
```

**Vérifier les alertes JSON :**

```bash
cat /var/log/rogue-dhcp-alerts.json
```

**Résultat :**

```json
{"timestamp": "2024-12-17T01:10:00", "type": "ROGUE_DHCP_DETECTED", "rogue_ip": "192.168.10.50", "rogue_mac": "00:0c:29:aa:bb:cc", "severity": "CRITICAL"}
```

**[OK] Rogue DHCP détecté avec succès !**

---

#### ÉTAPE 4 : Activer l'audit logging avec auditd

**`auditd`** = Linux Audit Daemon (audit du système)

**Permet de tracer toutes les modifications du système et des fichiers critiques.**

---

**Sur les serveurs DHCP (Primary et Secondary) :**

**1. Installer auditd :**

```bash
sudo apt install auditd audispd-plugins -y
```

---

**2. Activer auditd :**

```bash
sudo systemctl enable auditd
sudo systemctl start auditd
```

---

**3. Ajouter des règles d'audit pour DHCP :**

```bash
sudo nano /etc/audit/rules.d/dhcp-audit.rules
```

**Contenu :**

```bash
# ═══════════════════════════════════════════════════════════════
# RÈGLES D'AUDIT POUR DHCP
# ═══════════════════════════════════════════════════════════════

# Surveiller les modifications de la configuration DHCP
-w /etc/dhcp/dhcpd.conf -p wa -k dhcp_config_change
-w /etc/default/isc-dhcp-server -p wa -k dhcp_config_change
-w /etc/bind/ddns.key -p wa -k dhcp_ddns_key_change

# Explication :
# -w = Watch (surveiller)
# -p wa = Permissions : w (write), a (attribute change)
# -k = Key (étiquette pour identifier la règle)
#
# dhcp_config_change = Tag pour les modifications de config DHCP

# Surveiller les fichiers de baux
-w /var/lib/dhcp/dhcpd.leases -p wa -k dhcp_leases_change

# Surveiller le binaire du serveur DHCP
-w /usr/sbin/dhcpd -p x -k dhcp_execution

# Explication :
# -p x = Permission : x (execute)
# Alerte si le binaire dhcpd est exécuté

# Surveiller les connexions failover (port 847)
-a always,exit -F arch=b64 -S connect -F a2=0x034F -k dhcp_failover_connection

# Explication COMPLEXE :
# -a always,exit = Toujours auditer à la sortie d'un syscall
# -F arch=b64 = Filtre : architecture 64 bits
# -S connect = Syscall : connect() (connexion réseau)
# -F a2=0x034F = Filtre : argument 2 (adresse) contient port 847 (0x034F en hexa)
# -k dhcp_failover_connection = Tag
#
# 847 en hexadécimal :
# 847 décimal = 0x034F hexadécimal
# Sous Linux, les ports sont stockés en network byte order (big-endian)

# Surveiller les modifications du service systemd
-w /etc/systemd/system/isc-dhcp-server.service -p wa -k dhcp_service_change

# Surveiller les tentatives d'arrêt/démarrage du service DHCP
-a always,exit -F arch=b64 -S execve -F exe=/bin/systemctl -F a0=stop -F a1=isc-dhcp-server -k dhcp_service_stop
-a always,exit -F arch=b64 -S execve -F exe=/bin/systemctl -F a0=start -F a1=isc-dhcp-server -k dhcp_service_start
-a always,exit -F arch=b64 -S execve -F exe=/bin/systemctl -F a0=restart -F a1=isc-dhcp-server -k dhcp_service_restart

# Explication :
# -S execve = Syscall execve (exécution de programme)
# -F exe=/bin/systemctl = Filtre : le programme exécuté est systemctl
# -F a0=stop = Argument 0 (première commande) est "stop"
# -F a1=isc-dhcp-server = Argument 1 (nom du service) est "isc-dhcp-server"
```

**Sauvegarder.**

---

**4. Charger les règles :**

```bash
sudo augenrules --load
```

**Ou redémarrer auditd :**

```bash
sudo systemctl restart auditd
```

---

**5. Vérifier que les règles sont chargées :**

```bash
sudo auditctl -l
```

**Résultat (extrait) :**

```
-w /etc/dhcp/dhcpd.conf -p wa -k dhcp_config_change
-w /etc/default/isc-dhcp-server -p wa -k dhcp_config_change
-w /var/lib/dhcp/dhcpd.leases -p wa -k dhcp_leases_change
-w /usr/sbin/dhcpd -p x -k dhcp_execution
[...]
```

**[OK] Règles d'audit chargées !**

---

**6. Tester l'audit :**

**Modifier la configuration DHCP :**

```bash
sudo nano /etc/dhcp/dhcpd.conf
# Ajouter un commentaire quelconque
# Sauvegarder
```

**Consulter les logs d'audit :**

```bash
sudo ausearch -k dhcp_config_change
```

**Résultat :**

```
time->Mon Dec 17 01:30:00 2024
type=SYSCALL msg=audit(1702772400.123:456): arch=c000003e syscall=257 success=yes exit=3 a0=ffffff9c a1=7ffc12345678 a2=441 a3=1b6 items=2 ppid=12345 pid=12346 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=1 comm="nano" exe="/usr/bin/nano" key="dhcp_config_change"
type=PATH msg=audit(1702772400.123:456): item=0 name="/etc/dhcp/" inode=123456 dev=08:01 mode=040755 ouid=0 ogid=0 rdev=00:00 nametype=PARENT cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0
type=PATH msg=audit(1702772400.123:456): item=1 name="/etc/dhcp/dhcpd.conf" inode=789012 dev=08:01 mode=0100644 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0
type=PROCTITLE msg=audit(1702772400.123:456): proctitle=6E616E6F002F6574632F646863702F64686370642E636F6E66
```

**Points importants :**

**`key="dhcp_config_change"`**
- [OK] La règle a bien matché

**`comm="nano"`**
- [OK] L'outil utilisé pour modifier (nano)

**`name="/etc/dhcp/dhcpd.conf"`**
- [OK] Le fichier modifié

**`auid=1000`**
- UID de l'utilisateur qui a effectué la modification
- Permet de savoir QUI a modifié le fichier

---

**7. Générer un rapport d'audit :**

```bash
sudo aureport -k --summary
```

**Résultat :**

```
Key Report
===========================
12 dhcp_config_change
5 dhcp_service_restart
3 dhcp_leases_change
1 dhcp_execution
```

**Interprétation :**
- 12 modifications de configuration DHCP
- 5 redémarrages du service
- 3 modifications de fichier de baux
- 1 exécution du binaire dhcpd

**[OK] Audit complet de l'activité DHCP !**

---

Je continue avec la PARTIE 2 : MONITORING AVANCÉ (Prometheus, Grafana, etc.) ?

### PARTIE 2 : MONITORING AVANCÉ

#### ÉTAPE 5 : Installer et configurer Prometheus

**Prometheus** = Système de monitoring et d'alerting open-source

**Architecture :**
```
Prometheus Server
├─ Collecte les métriques (scraping)
├─ Stocke les time series
├─ Expose une API pour requêter
└─ Peut déclencher des alertes
```

---

**Sur un serveur dédié au monitoring (ou le serveur DHCP primary) :**

**1. Créer un utilisateur dédié :**

```bash
sudo useradd --no-create-home --shell /bin/false prometheus
```

**Explication :**
- `--no-create-home` : Pas de répertoire home (utilisateur système)
- `--shell /bin/false` : Pas de shell interactif (sécurité)

---

**2. Télécharger Prometheus :**

```bash
cd /tmp
wget https://github.com/prometheus/prometheus/releases/download/v2.48.0/prometheus-2.48.0.linux-amd64.tar.gz
```

**[ATTENTION] Vérifier la dernière version sur https://prometheus.io/download/**

---

**3. Extraire l'archive :**

```bash
tar -xvzf prometheus-2.48.0.linux-amd64.tar.gz
cd prometheus-2.48.0.linux-amd64
```

---

**4. Créer les répertoires nécessaires :**

```bash
sudo mkdir -p /etc/prometheus
sudo mkdir -p /var/lib/prometheus
```

**Explication :**
- `/etc/prometheus` : Fichiers de configuration
- `/var/lib/prometheus` : Données (time series)

---

**5. Copier les binaires :**

```bash
sudo cp prometheus /usr/local/bin/
sudo cp promtool /usr/local/bin/
```

**Explication :**
- `prometheus` : Serveur principal
- `promtool` : Outil de validation de configuration

---

**6. Copier les fichiers de configuration :**

```bash
sudo cp -r consoles /etc/prometheus/
sudo cp -r console_libraries /etc/prometheus/
```

---

**7. Définir les permissions :**

```bash
sudo chown -R prometheus:prometheus /etc/prometheus
sudo chown -R prometheus:prometheus /var/lib/prometheus
sudo chown prometheus:prometheus /usr/local/bin/prometheus
sudo chown prometheus:prometheus /usr/local/bin/promtool
```

---

**8. Créer la configuration Prometheus :**

```bash
sudo nano /etc/prometheus/prometheus.yml
```

**Contenu :**

```yaml
# ═══════════════════════════════════════════════════════════════
# CONFIGURATION PROMETHEUS POUR MONITORING DHCP
# ═══════════════════════════════════════════════════════════════

# ───────────────────────────────────────────────────────────────
# CONFIGURATION GLOBALE
# ───────────────────────────────────────────────────────────────

global:
  # Intervalle de scraping par défaut
  scrape_interval: 15s
  
  # Explication :
  # scrape_interval = Fréquence de collecte des métriques
  # 15s = Toutes les 15 secondes
  #
  # Compromis :
  # - Court (5s) = Métriques très précises, mais charge CPU élevée
  # - Long (60s) = Moins de charge, mais moins de granularité
  #
  # 15s = Bon compromis pour la plupart des usages
  
  # Intervalle d'évaluation des règles d'alerte
  evaluation_interval: 15s
  
  # Explication :
  # evaluation_interval = Fréquence d'évaluation des alertes
  # 15s = Vérifier les conditions d'alerte toutes les 15 secondes
  
  # Labels externes (ajoutés à toutes les métriques)
  external_labels:
    monitor: 'dhcp-monitoring'
    environment: 'production'
    datacenter: 'dakar'
  
  # Explication :
  # external_labels = Labels ajoutés à TOUTES les métriques
  # Utile pour :
  # - Identifier la source dans un système multi-Prometheus
  # - Filtrer dans Grafana
  # - Routage des alertes

# ───────────────────────────────────────────────────────────────
# CONFIGURATION DES ALERTES
# ───────────────────────────────────────────────────────────────

# Fichiers de règles d'alerte
rule_files:
  - "/etc/prometheus/rules/dhcp_alerts.yml"
  # - "/etc/prometheus/rules/node_alerts.yml"
  
  # Explication :
  # rule_files = Fichiers contenant les règles d'alerte
  # On les créera plus tard (ÉTAPE 8)

# ───────────────────────────────────────────────────────────────
# ALERTMANAGER
# ───────────────────────────────────────────────────────────────

alerting:
  alertmanagers:
    - static_configs:
        - targets:
            - 'localhost:9093'
  
  # Explication :
  # alertmanagers = Où envoyer les alertes
  # localhost:9093 = Alertmanager (on l'installera à l'ÉTAPE 8)

# ───────────────────────────────────────────────────────────────
# JOBS DE SCRAPING (COLLECTE DES MÉTRIQUES)
# ───────────────────────────────────────────────────────────────

scrape_configs:
  # ─────────────────────────────────────────────────────────────
  # Job 1 : Prometheus lui-même
  # ─────────────────────────────────────────────────────────────
  
  - job_name: 'prometheus'
    
    # Explication :
    # job_name = Identifiant du job
    # Les métriques auront le label job="prometheus"
    
    # Cibles à scraper
    static_configs:
      - targets: ['localhost:9090']
    
    # Explication :
    # targets = Liste des endpoints à scraper
    # localhost:9090 = Prometheus lui-même
    # Prometheus expose ses propres métriques sur :9090/metrics
    
    # Labels supplémentaires
    labels:
      instance: 'prometheus-server'
  
  # ─────────────────────────────────────────────────────────────
  # Job 2 : Node Exporter (serveurs DHCP)
  # ─────────────────────────────────────────────────────────────
  
  - job_name: 'node_exporter'
    
    # Explication :
    # Node Exporter = Exporte les métriques système
    # CPU, RAM, disque, réseau, etc.
    
    static_configs:
      # Serveur DHCP Primary
      - targets: ['192.168.10.10:9100']
        labels:
          instance: 'dhcp-primary'
          role: 'dhcp-server'
      
      # Serveur DHCP Secondary
      - targets: ['192.168.10.11:9100']
        labels:
          instance: 'dhcp-secondary'
          role: 'dhcp-server'
    
    # Explication :
    # targets = Serveurs à monitorer
    # :9100 = Port par défaut de node_exporter
    # labels = Labels custom pour identifier les serveurs
  
  # ─────────────────────────────────────────────────────────────
  # Job 3 : DHCP Exporter (métriques DHCP custom)
  # ─────────────────────────────────────────────────────────────
  
  - job_name: 'dhcp_exporter'
    
    # Explication :
    # DHCP Exporter = Exporter custom qu'on va créer (ÉTAPE 6)
    # Expose des métriques DHCP spécifiques :
    # - Nombre de baux actifs
    # - Taux d'utilisation du pool
    # - État du failover
    # - etc.
    
    scrape_interval: 30s
    
    # Explication :
    # scrape_interval = Fréquence spécifique pour ce job
    # 30s = Moins fréquent que le défaut (15s)
    # Les métriques DHCP changent moins vite que CPU/RAM
    
    static_configs:
      # DHCP Primary
      - targets: ['192.168.10.10:9101']
        labels:
          instance: 'dhcp-primary'
      
      # DHCP Secondary
      - targets: ['192.168.10.11:9101']
        labels:
          instance: 'dhcp-secondary'
    
    # Explication :
    # :9101 = Port custom pour notre DHCP exporter
    # (on choisit un port différent de node_exporter)

# ═══════════════════════════════════════════════════════════════
```

**Sauvegarder.**

---

**9. Créer le répertoire pour les règles d'alerte :**

```bash
sudo mkdir -p /etc/prometheus/rules
sudo chown prometheus:prometheus /etc/prometheus/rules
```

---

**10. Vérifier la configuration :**

```bash
promtool check config /etc/prometheus/prometheus.yml
```

**Résultat attendu :**

```
Checking /etc/prometheus/prometheus.yml
  SUCCESS: 0 rule files found
```

**[OK] Configuration valide !**

---

**11. Créer le service systemd :**

```bash
sudo nano /etc/systemd/system/prometheus.service
```

**Contenu :**

```ini
[Unit]
Description=Prometheus Monitoring System
Documentation=https://prometheus.io/docs/introduction/overview/
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=prometheus
Group=prometheus

# Commande de démarrage
ExecStart=/usr/local/bin/prometheus \
  --config.file=/etc/prometheus/prometheus.yml \
  --storage.tsdb.path=/var/lib/prometheus/ \
  --web.console.templates=/etc/prometheus/consoles \
  --web.console.libraries=/etc/prometheus/console_libraries \
  --web.listen-address=0.0.0.0:9090 \
  --storage.tsdb.retention.time=30d

# Explication des options :
#
# --config.file=/etc/prometheus/prometheus.yml
#   Fichier de configuration
#
# --storage.tsdb.path=/var/lib/prometheus/
#   Répertoire de stockage des données
#
# --web.console.templates=/etc/prometheus/consoles
#   Templates pour l'interface web
#
# --web.console.libraries=/etc/prometheus/console_libraries
#   Bibliothèques pour les consoles
#
# --web.listen-address=0.0.0.0:9090
#   Adresse d'écoute de l'interface web
#   0.0.0.0 = Toutes les interfaces
#   9090 = Port par défaut
#
# --storage.tsdb.retention.time=30d
#   Durée de rétention des données
#   30d = 30 jours
#   Ajuster selon l'espace disque disponible
#   Exemples : 7d, 60d, 1y

# Redémarrage automatique
Restart=always
RestartSec=10

# Limites de ressources
LimitNOFILE=65536

# Explication :
# LimitNOFILE = Nombre max de fichiers ouverts
# 65536 = Nécessaire pour Prometheus (beaucoup de fichiers)

[Install]
WantedBy=multi-user.target
```

**Sauvegarder.**

---

**12. Démarrer Prometheus :**

```bash
sudo systemctl daemon-reload
sudo systemctl enable prometheus
sudo systemctl start prometheus
```

**Vérifier l'état :**

```bash
sudo systemctl status prometheus
```

**Résultat attendu :**

```
[BLACK_CIRCLE] prometheus.service - Prometheus Monitoring System
     Loaded: loaded (/etc/systemd/system/prometheus.service; enabled)
     Active: active (running) since Mon 2024-12-17 02:00:00 UTC; 10s ago
```

**[OK] Prometheus démarré !**

---

**13. Autoriser le port 9090 dans le pare-feu :**

```bash
sudo ufw allow 9090/tcp comment 'Prometheus Web UI'
```

---

**14. Accéder à l'interface web Prometheus :**

**Ouvrir un navigateur : `http://192.168.10.10:9090`**

**Interface Prometheus :**
```
┌─────────────────────────────────────────────────────────────┐
│ Prometheus                                [Status] [Alerts] │
├─────────────────────────────────────────────────────────────┤
│ Expression (PromQL):                                        │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ up                                         [Execute]    │ │
│ └─────────────────────────────────────────────────────────┘ │
│                                                             │
│ Results:                                                    │
│ up{instance="localhost:9090", job="prometheus"} = 1        │
└─────────────────────────────────────────────────────────────┘
```

**Tester une requête PromQL :**

**Dans la barre de recherche, taper :**

```
up
```

**Cliquer sur "Execute".**

**Résultat :**

```
up{instance="localhost:9090", job="prometheus"} = 1
```

**Explication :**
- `up` = Métrique indiquant si une cible est accessible
- `1` = UP (accessible)
- `0` = DOWN (inaccessible)

**[OK] Prometheus fonctionne !**

---

**15. Vérifier les targets (cibles) :**

**Dans Prometheus UI, aller dans : Status -> Targets**

**Résultat attendu :**

```
Job: prometheus
└─ http://localhost:9090/metrics
   State: UP
   Last Scrape: 2s ago

Job: node_exporter
└─ http://192.168.10.10:9100/metrics
   State: DOWN (connection refused)
   └─ http://192.168.10.11:9100/metrics
   State: DOWN (connection refused)

Job: dhcp_exporter
└─ http://192.168.10.10:9101/metrics
   State: DOWN (connection refused)
   └─ http://192.168.10.11:9101/metrics
   State: DOWN (connection refused)
```

**Normal que node_exporter et dhcp_exporter soient DOWN : on ne les a pas encore installés !**

---

#### ÉTAPE 6 : Installer Node Exporter et créer un DHCP Exporter

##### 6.1 - Installer Node Exporter (métriques système)

**Sur les serveurs DHCP (Primary et Secondary) :**

**1. Télécharger Node Exporter :**

```bash
cd /tmp
wget https://github.com/prometheus/node_exporter/releases/download/v1.7.0/node_exporter-1.7.0.linux-amd64.tar.gz
```

---

**2. Extraire :**

```bash
tar -xvzf node_exporter-1.7.0.linux-amd64.tar.gz
cd node_exporter-1.7.0.linux-amd64
```

---

**3. Copier le binaire :**

```bash
sudo cp node_exporter /usr/local/bin/
sudo chown prometheus:prometheus /usr/local/bin/node_exporter
```

---

**4. Créer le service systemd :**

```bash
sudo nano /etc/systemd/system/node_exporter.service
```

**Contenu :**

```ini
[Unit]
Description=Node Exporter
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=prometheus
Group=prometheus

ExecStart=/usr/local/bin/node_exporter \
  --web.listen-address=:9100 \
  --collector.filesystem.mount-points-exclude='^/(dev|proc|sys|var/lib/docker/.+|var/lib/kubelet/.+)($|/)' \
  --collector.filesystem.fs-types-exclude='^(autofs|binfmt_misc|bpf|cgroup2?|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|iso9660|mqueue|nsfs|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|selinuxfs|squashfs|sysfs|tracefs)$'

# Explication :
# --web.listen-address=:9100
#   Port d'écoute (9100 par défaut)
#
# --collector.filesystem.mount-points-exclude='^/(dev|proc|sys|...)'
#   Exclure certains points de montage (filesystem virtuels)
#
# --collector.filesystem.fs-types-exclude='^(autofs|...)'
#   Exclure certains types de filesystems

Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
```

**Sauvegarder.**

---

**5. Démarrer Node Exporter :**

```bash
sudo systemctl daemon-reload
sudo systemctl enable node_exporter
sudo systemctl start node_exporter
```

**Vérifier :**

```bash
sudo systemctl status node_exporter
```

**Résultat attendu : `Active: active (running)`**

---

**6. Autoriser le port 9100 :**

```bash
sudo ufw allow from 192.168.10.10 to any port 9100 proto tcp comment 'Prometheus Node Exporter'
```

---

**7. Tester l'endpoint des métriques :**

```bash
curl http://localhost:9100/metrics | head -20
```

**Résultat (extrait) :**

```
# HELP node_cpu_seconds_total Seconds the CPUs spent in each mode.
# TYPE node_cpu_seconds_total counter
node_cpu_seconds_total{cpu="0",mode="idle"} 1234567.89
node_cpu_seconds_total{cpu="0",mode="system"} 12345.67
node_cpu_seconds_total{cpu="0",mode="user"} 23456.78

# HELP node_memory_MemTotal_bytes Memory information field MemTotal_bytes.
# TYPE node_memory_MemTotal_bytes gauge
node_memory_MemTotal_bytes 4.294967296e+09

# HELP node_network_receive_bytes_total Network device statistic receive_bytes.
# TYPE node_network_receive_bytes_total counter
node_network_receive_bytes_total{device="ens33"} 1.234567890123e+12
```

**[OK] Node Exporter fonctionne !**

---

**8. Vérifier dans Prometheus :**

**Aller dans Prometheus UI -> Status -> Targets**

**Le target `node_exporter` devrait maintenant être UP.**

---

##### 6.2 - Créer un DHCP Exporter custom

**On va créer un exporter Python qui expose des métriques DHCP spécifiques.**

**Sur les serveurs DHCP (Primary et Secondary) :**

**1. Installer les dépendances Python :**

```bash
sudo apt install python3 python3-pip -y
sudo pip3 install prometheus_client
```

---

**2. Créer le script DHCP Exporter :**

```bash
sudo nano /usr/local/bin/dhcp_exporter.py
```

**Contenu :**

```python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
DHCP Exporter pour Prometheus
Expose des métriques DHCP custom
"""

from prometheus_client import start_http_server, Gauge, Counter, Info
import time
import re
import subprocess
import os

# ═══════════════════════════════════════════════════════════════
# CONFIGURATION
# ═══════════════════════════════════════════════════════════════

LEASES_FILE = "/var/lib/dhcp/dhcpd.leases"
CONFIG_FILE = "/etc/dhcp/dhcpd.conf"
PORT = 9101

# ═══════════════════════════════════════════════════════════════
# DÉFINITION DES MÉTRIQUES PROMETHEUS
# ═══════════════════════════════════════════════════════════════

# Métriques Gauge (valeur instantanée)

# Nombre de baux actifs
dhcp_active_leases = Gauge(
    'dhcp_active_leases',
    'Number of active DHCP leases',
    ['pool']
)

# Explication :
# Gauge = Métrique qui peut monter et descendre
# 'dhcp_active_leases' = Nom de la métrique
# 'Number of active...' = Description
# ['pool'] = Labels (permettent de différencier par pool)

# Nombre total de baux (actifs + expirés)
dhcp_total_leases = Gauge(
    'dhcp_total_leases',
    'Total number of DHCP leases (active + expired)'
)

# Taux d'utilisation du pool (pourcentage)
dhcp_pool_utilization = Gauge(
    'dhcp_pool_utilization',
    'DHCP pool utilization percentage',
    ['pool']
)

# Nombre d'adresses disponibles dans le pool
dhcp_pool_available = Gauge(
    'dhcp_pool_available',
    'Number of available addresses in pool',
    ['pool']
)

# Nombre total d'adresses dans le pool
dhcp_pool_size = Gauge(
    'dhcp_pool_size',
    'Total size of DHCP pool',
    ['pool']
)

# État du serveur DHCP (1 = running, 0 = stopped)
dhcp_server_status = Gauge(
    'dhcp_server_status',
    'DHCP server status (1=running, 0=stopped)'
)

# État du failover (1 = NORMAL, 0 = autre)
dhcp_failover_status = Gauge(
    'dhcp_failover_status',
    'DHCP failover status (1=NORMAL, 0=other)',
    ['peer']
)

# État local du failover (code numérique)
dhcp_failover_local_state = Gauge(
    'dhcp_failover_local_state',
    'DHCP failover local state code',
    ['peer', 'state_name']
)

# État du partenaire failover (code numérique)
dhcp_failover_partner_state = Gauge(
    'dhcp_failover_partner_state',
    'DHCP failover partner state code',
    ['peer', 'state_name']
)

# Métriques Counter (valeur cumulative, ne peut qu'augmenter)

# Nombre total d'attributions depuis le démarrage
dhcp_leases_issued_total = Counter(
    'dhcp_leases_issued_total',
    'Total number of DHCP leases issued since start'
)

# Explication :
# Counter = Métrique qui ne peut qu'augmenter
# Utilisée pour compter des événements
# Prometheus peut calculer le taux (rate) automatiquement

# Métriques Info (métadonnées)

# Version du serveur DHCP
dhcp_server_info = Info(
    'dhcp_server',
    'DHCP server information'
)

# Explication :
# Info = Métrique pour stocker des informations textuelles
# Exemple : version, hostname, etc.

# ═══════════════════════════════════════════════════════════════
# FONCTIONS DE COLLECTE DES MÉTRIQUES
# ═══════════════════════════════════════════════════════════════

def parse_leases_file():
    """
    Parse le fichier dhcpd.leases et retourne les statistiques
    """
    active_leases = 0
    total_leases = 0
    
    if not os.path.exists(LEASES_FILE):
        return active_leases, total_leases
    
    try:
        with open(LEASES_FILE, 'r') as f:
            content = f.read()
        
        # Compter les baux actifs
        active_leases = content.count('binding state active')
        
        # Compter tous les baux (lease <IP> {)
        total_leases = len(re.findall(r'lease \d+\.\d+\.\d+\.\d+ \{', content))
        
    except Exception as e:
        print(f"Erreur lors de la lecture de {LEASES_FILE}: {e}")
    
    return active_leases, total_leases

def parse_config_file():
    """
    Parse dhcpd.conf pour extraire la taille du pool
    """
    pool_size = 0
    
    if not os.path.exists(CONFIG_FILE):
        return pool_size
    
    try:
        with open(CONFIG_FILE, 'r') as f:
            content = f.read()
        
        # Chercher : range 192.168.10.100 192.168.10.200;
        match = re.search(r'range\s+(\d+\.\d+\.\d+\.)(\d+)\s+(\d+\.\d+\.\d+\.)(\d+)', content)
        
        if match:
            # Extraire les deux derniers octets
            start_ip = int(match.group(2))
            end_ip = int(match.group(4))
            
            # Calculer la taille du pool
            pool_size = end_ip - start_ip + 1
    
    except Exception as e:
        print(f"Erreur lors de la lecture de {CONFIG_FILE}: {e}")
    
    return pool_size

def get_dhcp_service_status():
    """
    Vérifie si le service DHCP tourne
    """
    try:
        result = subprocess.run(
            ['systemctl', 'is-active', 'isc-dhcp-server'],
            capture_output=True,
            text=True
        )
        
        # Retourne 1 si actif, 0 sinon
        return 1 if result.stdout.strip() == 'active' else 0
    
    except Exception as e:
        print(f"Erreur lors de la vérification du service: {e}")
        return 0

def get_failover_status():
    """
    Récupère l'état du failover via omshell
    """
    try:
        # Commandes omshell
        omshell_commands = """
server 127.0.0.1
connect
new failover-state
set name = "dhcp-failover"
open
"""
        
        # Exécuter omshell
        result = subprocess.run(
            ['omshell'],
            input=omshell_commands,
            capture_output=True,
            text=True,
            timeout=5
        )
        
        output = result.stdout
        
        # Extraire l'état local
        local_match = re.search(r'local-state\s*=\s*([0-9a-f]+)', output)
        partner_match = re.search(r'partner-state\s*=\s*([0-9a-f]+)', output)
        
        local_state = int(local_match.group(1), 16) if local_match else None
        partner_state = int(partner_match.group(1), 16) if partner_match else None
        
        # Map des états
        state_names = {
            1: 'STARTUP',
            2: 'NORMAL',
            3: 'COMMUNICATIONS-INTERRUPTED',
            4: 'PARTNER-DOWN',
            5: 'POTENTIAL-CONFLICT',
            6: 'RECOVER',
            7: 'PAUSED',
            8: 'SHUTDOWN',
            9: 'RESOLUTION-INTERRUPTED',
            10: 'CONFLICT-DONE',
            255: 'UNKNOWN'
        }
        
        return local_state, partner_state, state_names
    
    except Exception as e:
        print(f"Erreur lors de la récupération de l'état failover: {e}")
        return None, None, {}

def get_dhcp_version():
    """
    Récupère la version du serveur DHCP
    """
    try:
        result = subprocess.run(
            ['dhcpd', '--version'],
            capture_output=True,
            text=True,
            stderr=subprocess.STDOUT
        )
        
        # dhcpd affiche la version sur stderr
        version_match = re.search(r'isc-dhcp-(\S+)', result.stdout)
        
        if version_match:
            return version_match.group(1)
        
        return "unknown"
    
    except Exception as e:
        print(f"Erreur lors de la récupération de la version: {e}")
        return "unknown"

# ═══════════════════════════════════════════════════════════════
# FONCTION DE MISE À JOUR DES MÉTRIQUES
# ═══════════════════════════════════════════════════════════════

def update_metrics():
    """
    Met à jour toutes les métriques
    """
    # 1. Parser le fichier de baux
    active, total = parse_leases_file()
    dhcp_active_leases.labels(pool='main').set(active)
    dhcp_total_leases.set(total)
    
    # 2. Parser la config pour la taille du pool
    pool_size = parse_config_file()
    dhcp_pool_size.labels(pool='main').set(pool_size)
    
    # 3. Calculer le taux d'utilisation
    if pool_size > 0:
        utilization = (active / pool_size) * 100
        dhcp_pool_utilization.labels(pool='main').set(utilization)
        
        available = pool_size - active
        dhcp_pool_available.labels(pool='main').set(available)
    
    # 4. État du service DHCP
    service_status = get_dhcp_service_status()
    dhcp_server_status.set(service_status)
    
    # 5. État du failover
    local_state, partner_state, state_names = get_failover_status()
    
    if local_state is not None:
        # État global (1 si NORMAL, 0 sinon)
        is_normal = 1 if local_state == 2 and partner_state == 2 else 0
        dhcp_failover_status.labels(peer='dhcp-failover').set(is_normal)
        
        # État détaillé
        local_state_name = state_names.get(local_state, 'UNKNOWN')
        partner_state_name = state_names.get(partner_state, 'UNKNOWN')
        
        dhcp_failover_local_state.labels(
            peer='dhcp-failover',
            state_name=local_state_name
        ).set(local_state)
        
        dhcp_failover_partner_state.labels(
            peer='dhcp-failover',
            state_name=partner_state_name
        ).set(partner_state)
    
    # 6. Informations sur le serveur
    version = get_dhcp_version()
    dhcp_server_info.info({
        'version': version,
        'hostname': os.uname().nodename
    })

# ═══════════════════════════════════════════════════════════════
# FONCTION PRINCIPALE
# ═══════════════════════════════════════════════════════════════

def main():
    """
    Point d'entrée principal
    """
    print(f"[RAPIDE] Démarrage du DHCP Exporter sur le port {PORT}")
    
    # Démarrer le serveur HTTP Prometheus
    start_http_server(PORT)
    
    # Explication :
    # start_http_server(PORT) démarre un serveur HTTP
    # qui expose les métriques sur http://localhost:9101/metrics
    
    print(f"[GRAPHIQUE] Métriques disponibles sur http://localhost:{PORT}/metrics")
    
    # Boucle infinie pour mettre à jour les métriques
    while True:
        try:
            update_metrics()
            # Attendre 30 secondes avant la prochaine mise à jour
            time.sleep(30)
        
        except KeyboardInterrupt:
            print("\n[WAVING_HAND_SIGN] Arrêt du DHCP Exporter")
            break
        
        except Exception as e:
            print(f"[X] Erreur : {e}")
            time.sleep(30)

if __name__ == "__main__":
    main()
```

**Rendre exécutable :**

```bash
sudo chmod +x /usr/local/bin/dhcp_exporter.py
```

---

**3. Créer le service systemd :**

```bash
sudo nano /etc/systemd/system/dhcp-exporter.service
```

**Contenu :**

```ini
[Unit]
Description=DHCP Exporter for Prometheus
After=network-online.target isc-dhcp-server.service
Wants=network-online.target

[Service]
Type=simple
User=root
Group=root

# Commande de démarrage
ExecStart=/usr/bin/python3 /usr/local/bin/dhcp_exporter.py

# Redémarrage automatique
Restart=always
RestartSec=10

# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=dhcp-exporter

[Install]
WantedBy=multi-user.target
```

**Sauvegarder.**

---

**4. Démarrer le DHCP Exporter :**

```bash
sudo systemctl daemon-reload
sudo systemctl enable dhcp-exporter
sudo systemctl start dhcp-exporter
```

**Vérifier :**

```bash
sudo systemctl status dhcp-exporter
```

**Résultat attendu :**

```
[BLACK_CIRCLE] dhcp-exporter.service - DHCP Exporter for Prometheus
     Loaded: loaded (/etc/systemd/system/dhcp-exporter.service; enabled)
     Active: active (running) since Mon 2024-12-17 03:00:00 UTC; 10s ago
```

---

**5. Autoriser le port 9101 :**

```bash
sudo ufw allow from 192.168.10.10 to any port 9101 proto tcp comment 'Prometheus DHCP Exporter'
```

---

**6. Tester l'endpoint des métriques :**

```bash
curl http://localhost:9101/metrics
```

**Résultat (extrait) :**

```
# HELP dhcp_active_leases Number of active DHCP leases
# TYPE dhcp_active_leases gauge
dhcp_active_leases{pool="main"} 15.0

# HELP dhcp_total_leases Total number of DHCP leases (active + expired)
# TYPE dhcp_total_leases gauge
dhcp_total_leases 23.0

# HELP dhcp_pool_utilization DHCP pool utilization percentage
# TYPE dhcp_pool_utilization gauge
dhcp_pool_utilization{pool="main"} 14.851485148514851

# HELP dhcp_pool_available Number of available addresses in pool
# TYPE dhcp_pool_available gauge
dhcp_pool_available{pool="main"} 86.0

# HELP dhcp_pool_size Total size of DHCP pool
# TYPE dhcp_pool_size gauge
dhcp_pool_size{pool="main"} 101.0

# HELP dhcp_server_status DHCP server status (1=running, 0=stopped)
# TYPE dhcp_server_status gauge
dhcp_server_status 1.0

# HELP dhcp_failover_status DHCP failover status (1=NORMAL, 0=other)
# TYPE dhcp_failover_status gauge
dhcp_failover_status{peer="dhcp-failover"} 1.0

# HELP dhcp_failover_local_state DHCP failover local state code
# TYPE dhcp_failover_local_state gauge
dhcp_failover_local_state{peer="dhcp-failover",state_name="NORMAL"} 2.0

# HELP dhcp_failover_partner_state DHCP failover partner state code
# TYPE dhcp_failover_partner_state gauge
dhcp_failover_partner_state{peer="dhcp-failover",state_name="NORMAL"} 2.0

# HELP dhcp_server_info DHCP server information
# TYPE dhcp_server_info gauge
dhcp_server_info{hostname="dhcp-primary",version="4.4.1"} 1.0
```

**[OK] DHCP Exporter fonctionne !**

---

**7. Vérifier dans Prometheus :**

**Aller dans Prometheus UI -> Status -> Targets**

**Le target `dhcp_exporter` devrait maintenant être UP.**

---

**8. Tester une requête PromQL :**

**Dans Prometheus UI, taper :**

```
dhcp_pool_utilization
```

**Résultat :**

```
dhcp_pool_utilization{instance="dhcp-primary", job="dhcp_exporter", pool="main"} = 14.85
dhcp_pool_utilization{instance="dhcp-secondary", job="dhcp_exporter", pool="main"} = 14.85
```

**[OK] Métriques DHCP custom disponibles !**

---

Je continue avec l'installation de Grafana et la création de dashboards (ÉTAPE 7) ?

#### ÉTAPE 7 : Installer Grafana et créer des dashboards

**Grafana** = Plateforme de visualisation et d'analyse de métriques

**Utilité :**
- Créer des dashboards interactifs
- Visualiser les métriques Prometheus
- Alertes visuelles
- Rapports PDF

---

**Sur le serveur de monitoring (même serveur que Prometheus) :**

**1. Ajouter le dépôt Grafana :**

```bash
# Installer les dépendances
sudo apt install -y software-properties-common wget

# Ajouter la clé GPG de Grafana
wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -

# Ajouter le dépôt
echo "deb https://packages.grafana.com/oss/deb stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
```

---

**2. Installer Grafana :**

```bash
sudo apt update
sudo apt install grafana -y
```

---

**3. Démarrer Grafana :**

```bash
sudo systemctl daemon-reload
sudo systemctl enable grafana-server
sudo systemctl start grafana-server
```

**Vérifier l'état :**

```bash
sudo systemctl status grafana-server
```

**Résultat attendu :**

```
[BLACK_CIRCLE] grafana-server.service - Grafana instance
     Loaded: loaded (/lib/systemd/system/grafana-server.service; enabled)
     Active: active (running) since Mon 2024-12-17 03:30:00 UTC; 10s ago
```

**[OK] Grafana démarré !**

---

**4. Autoriser le port 3000 (Grafana) :**

```bash
sudo ufw allow 3000/tcp comment 'Grafana Web UI'
```

---

**5. Accéder à l'interface web Grafana :**

**Ouvrir un navigateur : `http://192.168.10.10:3000`**

**Écran de connexion :**

```
┌─────────────────────────────────────┐
│         Welcome to Grafana          │
├─────────────────────────────────────┤
│ Email or username:                  │
│ ┌─────────────────────────────────┐ │
│ │ admin                           │ │
│ └─────────────────────────────────┘ │
│                                     │
│ Password:                           │
│ ┌─────────────────────────────────┐ │
│ │ admin                           │ │
│ └─────────────────────────────────┘ │
│                                     │
│          [Log in]                   │
└─────────────────────────────────────┘
```

**Identifiants par défaut :**
- **Username :** `admin`
- **Password :** `admin`

**Se connecter.**

---

**6. Changer le mot de passe :**

**Grafana demandera de changer le mot de passe au premier login.**

**Définir un nouveau mot de passe fort.**

**Exemple : `GrafanaDHCP2024!`**

---

**7. Ajouter Prometheus comme source de données :**

**Dans Grafana :**

**Navigation : Configuration (icône engrenage) -> Data sources**

**Cliquer sur "Add data source"**

**Sélectionner "Prometheus"**

**Configuration :**

```
Name: Prometheus
Default: [OK] (cocher)

HTTP:
  URL: http://localhost:9090
  Access: Server (default)

Auth:
  (Tout décocher)

Prometheus details:
  Scrape interval: 15s
  Query timeout: 60s
  HTTP Method: POST
```

**Cliquer sur "Save & Test"**

**Résultat attendu :**

```
[OK] Data source is working
```

**[OK] Prometheus connecté à Grafana !**

---

**8. Créer le dashboard DHCP - Vue d'ensemble :**

**Dans Grafana :**

**Navigation : Dashboards (icône carrés) -> New -> New Dashboard**

**Cliquer sur "Add visualization"**

**Sélectionner "Prometheus"**

---

##### Panel 1 : Utilisation du pool DHCP (Gauge)

**Configuration du panel :**

**Query (PromQL) :**

```promql
dhcp_pool_utilization{pool="main"}
```

**Explication :**
- `dhcp_pool_utilization` = Métrique du taux d'utilisation
- `{pool="main"}` = Filtre sur le pool principal
- Résultat : Pourcentage d'utilisation (ex: 14.85%)

---

**Panel options (onglet à droite) :**

```
Title: Utilisation du Pool DHCP
Description: Pourcentage d'adresses IP utilisées
```

---

**Visualization (type de graphique) :**

**Sélectionner "Gauge" (jauge)**

**Options du Gauge :**

```
Show threshold labels: [OK]
Show threshold markers: [OK]

Thresholds:
  Base: Green
  80: Orange
  90: Red
```

**Explication :**
- < 80% = Vert (OK)
- 80-90% = Orange (Attention)
- > 90% = Rouge (Critique)

---

**Unit (unité) :**

```
Standard options -> Unit -> Misc -> Percent (0-100)
```

---

**Cliquer sur "Apply" (en haut à droite)**

**Le panel apparaît dans le dashboard.**

---

##### Panel 2 : Baux actifs vs Total (Stat)

**Ajouter un nouveau panel : "Add -> Visualization"**

**Query A :**

```promql
dhcp_active_leases{pool="main"}
```

**Query B :**

```promql
dhcp_pool_size{pool="main"}
```

---

**Panel options :**

```
Title: Baux Actifs / Total
```

---

**Visualization : Stat**

**Options :**

```
Text mode: Value and name
Graph mode: None
Color mode: Background
Orientation: Horizontal

Value options:
  Show: All values
  Calculate: Last
```

---

**Transformations (onglet Transform) :**

**Ajouter transformation : "Add field from calculation"**

```
Mode: Binary operation
Operation: Divide
Field: dhcp_active_leases
By field: dhcp_pool_size
Alias: Utilization
```

**Puis transformation : "Organize fields"**

```
Renommer :
  dhcp_active_leases -> Actifs
  dhcp_pool_size -> Total
  Utilization -> Taux
```

---

**Cliquer sur "Apply"**

---

##### Panel 3 : Historique d'utilisation (Time series)

**Ajouter un nouveau panel**

**Query :**

```promql
dhcp_pool_utilization{pool="main"}
```

---

**Panel options :**

```
Title: Historique d'utilisation du pool (24h)
```

---

**Visualization : Time series (graphique temporel)**

**Options :**

```
Legend:
  Mode: List
  Placement: Bottom
  Values: Last, Min, Max

Graph styles:
  Style: Line
  Line interpolation: Smooth
  Line width: 2
  Fill opacity: 20
  Gradient mode: Opacity
  Show points: Auto

Axis:
  Placement: Auto
  Label: Utilisation (%)
  Soft min: 0
  Soft max: 100
```

---

**Thresholds :**

```
Base: Green
80: Orange
90: Red
```

---

**Cliquer sur "Apply"**

---

##### Panel 4 : État du Failover (Stat)

**Ajouter un nouveau panel**

**Query A (État global) :**

```promql
dhcp_failover_status{peer="dhcp-failover"}
```

**Query B (État local) :**

```promql
dhcp_failover_local_state{peer="dhcp-failover"}
```

**Query C (État partenaire) :**

```promql
dhcp_failover_partner_state{peer="dhcp-failover"}
```

---

**Panel options :**

```
Title: État du Failover DHCP
```

---

**Visualization : Stat**

**Value mappings (mapper les valeurs) :**

**Pour Query A (dhcp_failover_status) :**

```
Value mappings:
  1 -> NORMAL [OK] (Vert)
  0 -> DEGRADED [ATTENTION] (Orange/Rouge)
```

**Pour Query B et C (états numériques) :**

```
Value mappings:
  1 -> STARTUP
  2 -> NORMAL
  3 -> COMM-INTERRUPTED
  4 -> PARTNER-DOWN
  5 -> POTENTIAL-CONFLICT
  6 -> RECOVER
  7 -> PAUSED
  8 -> SHUTDOWN
  9 -> RESOLUTION-INTERRUPTED
  10 -> CONFLICT-DONE
  255 -> UNKNOWN
```

---

**Thresholds (pour Query B et C) :**

```
Base: Orange (par défaut)
2: Green (NORMAL)
4: Red (PARTNER-DOWN)
3: Orange (COMM-INTERRUPTED)
```

---

**Cliquer sur "Apply"**

---

##### Panel 5 : Adresses disponibles (Bar gauge)

**Ajouter un nouveau panel**

**Query :**

```promql
dhcp_pool_available{pool="main"}
```

---

**Panel options :**

```
Title: Adresses IP Disponibles
```

---

**Visualization : Bar gauge (jauge horizontale)**

**Options :**

```
Orientation: Horizontal
Display mode: Gradient
Show unfilled area: [OK]

Value options:
  Show: Calculate
  Calculation: Last

Thresholds:
  Base: Red
  20: Orange
  50: Green
```

**Explication :**
- < 20 adresses = Rouge (Critique)
- 20-50 adresses = Orange (Attention)
- > 50 adresses = Vert (OK)

---

**Unit :**

```
Standard options -> Unit -> Misc -> Short
```

---

**Cliquer sur "Apply"**

---

##### Panel 6 : Serveurs DHCP - CPU et Mémoire (Time series)

**Ajouter un nouveau panel**

**Query A (CPU Primary) :**

```promql
100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle", instance="dhcp-primary"}[5m])) * 100)
```

**Explication COMPLEXE :**

**`node_cpu_seconds_total{mode="idle"}`**
- Secondes passées en mode idle (inactif)
- Plus le CPU est utilisé, moins il est idle

**`irate(...[5m])`**
- Calcule le taux de variation sur 5 minutes
- irate = instant rate (taux instantané)

**`avg by (instance) (...)`**
- Moyenne par instance (serveur)

**`100 - (... * 100)`**
- Convertir en pourcentage d'utilisation
- 100% - idle% = utilisé%

---

**Query B (CPU Secondary) :**

```promql
100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle", instance="dhcp-secondary"}[5m])) * 100)
```

---

**Query C (Mémoire Primary) :**

```promql
100 * (1 - ((node_memory_MemAvailable_bytes{instance="dhcp-primary"} or node_memory_MemFree_bytes{instance="dhcp-primary"}) / node_memory_MemTotal_bytes{instance="dhcp-primary"}))
```

**Explication :**

**`node_memory_MemAvailable_bytes`**
- Mémoire disponible (incluant cache/buffers)

**`node_memory_MemTotal_bytes`**
- Mémoire totale

**`1 - (Available / Total)`**
- Proportion de mémoire utilisée

**`100 * (...)`**
- Convertir en pourcentage

---

**Query D (Mémoire Secondary) :**

```promql
100 * (1 - ((node_memory_MemAvailable_bytes{instance="dhcp-secondary"} or node_memory_MemFree_bytes{instance="dhcp-secondary"}) / node_memory_MemTotal_bytes{instance="dhcp-secondary"}))
```

---

**Panel options :**

```
Title: Ressources Serveurs DHCP
```

---

**Visualization : Time series**

**Legend :**

```
Mode: Table
Placement: Bottom
Values: Last, Min, Max, Mean

Legend values :
  Query A -> CPU Primary
  Query B -> CPU Secondary
  Query C -> RAM Primary
  Query D -> RAM Secondary
```

---

**Unit :**

```
Standard options -> Unit -> Misc -> Percent (0-100)
```

---

**Thresholds :**

```
Base: Green
70: Yellow
90: Red
```

---

**Cliquer sur "Apply"**

---

##### Panel 7 : Top 10 clients DHCP (Table)

**[ATTENTION] Ce panel nécessite de parser les logs DHCP.**

**Pour simplifier, on va afficher les métriques de base dans une table.**

**Ajouter un nouveau panel**

**Query :**

```promql
dhcp_active_leases{pool="main"}
```

---

**Panel options :**

```
Title: Statistiques Pools DHCP
```

---

**Visualization : Table**

**Affichera les métriques sous forme de tableau.**

---

**Cliquer sur "Apply"**

---

##### Organisation du dashboard

**Réorganiser les panels :**

**Ligne 1 (métriques principales) :**
- Panel 1 : Utilisation du Pool (Gauge)
- Panel 2 : Baux Actifs/Total (Stat)
- Panel 4 : État Failover (Stat)

**Ligne 2 (graphiques temporels) :**
- Panel 3 : Historique d'utilisation (Time series - pleine largeur)

**Ligne 3 (ressources) :**
- Panel 6 : CPU et Mémoire serveurs (Time series - pleine largeur)

**Ligne 4 (détails) :**
- Panel 5 : Adresses disponibles (Bar gauge)
- Panel 7 : Statistiques (Table)

---

**Glisser-déposer les panels pour les organiser.**

---

**9. Sauvegarder le dashboard :**

**Cliquer sur l'icône disquette (en haut à droite)**

**Dashboard settings :**

```
Name: DHCP Monitoring - Vue d'ensemble
Folder: General (ou créer un dossier "DHCP")
Tags: dhcp, monitoring, network

Time options:
  Timezone: Browser Time
  Auto refresh: 30s

Variables: (on ajoutera plus tard)
```

**Cliquer sur "Save"**

**[OK] Dashboard créé !**

---

**10. Créer un dashboard détaillé pour le Failover :**

**Créer un nouveau dashboard : Dashboards -> New -> New Dashboard**

---

##### Panel 1 : États du Failover (Timeline)

**Ajouter un panel**

**Query A (État local) :**

```promql
dhcp_failover_local_state{peer="dhcp-failover"}
```

**Query B (État partenaire) :**

```promql
dhcp_failover_partner_state{peer="dhcp-failover"}
```

---

**Panel options :**

```
Title: Timeline des États Failover
```

---

**Visualization : State timeline**

**Permet de voir l'évolution des états dans le temps.**

---

**Value mappings :**

```
1 -> STARTUP (Bleu clair)
2 -> NORMAL (Vert)
3 -> COMM-INTERRUPTED (Orange)
4 -> PARTNER-DOWN (Rouge)
5 -> POTENTIAL-CONFLICT (Violet)
6 -> RECOVER (Jaune)
...
```

---

**Cliquer sur "Apply"**

---

##### Panel 2 : Synchronisation Failover (Graph)

**Ajouter un panel**

**Query (si disponible via logs/métriques custom) :**

**Pour cet exercice, on va simuler avec :**

```promql
rate(dhcp_active_leases{pool="main"}[5m])
```

**Cela montre le taux de changement des baux (indicateur de synchronisation active).**

---

**Panel options :**

```
Title: Activité de Synchronisation
```

---

**Visualization : Time series**

---

**Cliquer sur "Apply"**

---

**11. Sauvegarder ce dashboard :**

```
Name: DHCP Failover - Détails
Folder: DHCP
Tags: dhcp, failover, ha
```

---

**12. Créer un dashboard pour l'audit et la sécurité :**

**Nouveau dashboard**

---

##### Panel 1 : Logs d'audit DHCP (Logs)

**[ATTENTION] Nécessite Loki (pas encore installé)**

**On le fera à l'ÉTAPE suivante.**

**Pour l'instant, créer un panel de type "Logs" vide.**

---

##### Panel 2 : Détection Rogue DHCP (Stat)

**Si on a intégré le script de détection rogue DHCP avec Prometheus, on peut créer une métrique.**

**Sinon, panel informatif pour l'instant.**

---

**Sauvegarder le dashboard :**

```
Name: DHCP Sécurité et Audit
Folder: DHCP
```

---

**13. Créer des variables pour filtrer les dashboards :**

**Dans le dashboard "DHCP Monitoring - Vue d'ensemble"**

**Settings (icône engrenage) -> Variables -> Add variable**

---

**Variable 1 : Serveur DHCP**

```
Name: dhcp_server
Label: Serveur DHCP
Type: Query

Data source: Prometheus

Query:
  label_values(dhcp_active_leases, instance)

Explication :
  label_values(métrique, label) retourne toutes les valeurs distinctes du label
  Résultat : "dhcp-primary", "dhcp-secondary"

Refresh: On time range change

Multi-value: [OK]
Include All option: [OK]
```

**Cliquer sur "Add"**

---

**Utiliser la variable dans les queries :**

**Modifier les queries des panels pour utiliser `$dhcp_server` :**

**Avant :**

```promql
dhcp_active_leases{pool="main", instance="dhcp-primary"}
```

**Après :**

```promql
dhcp_active_leases{pool="main", instance=~"$dhcp_server"}
```

**Explication :**
- `$dhcp_server` = Variable Grafana
- `=~` = Regex match (pour multi-value)
- Si "All" sélectionné -> Affiche tous les serveurs
- Si "dhcp-primary" sélectionné -> Affiche seulement primary

---

**Variable 2 : Pool DHCP**

```
Name: pool
Label: Pool DHCP
Type: Query

Query:
  label_values(dhcp_active_leases, pool)

Résultat : "main" (ou autres pools si configurés)
```

---

**Mettre à jour toutes les queries pour utiliser `$pool` :**

```promql
dhcp_active_leases{pool=~"$pool", instance=~"$dhcp_server"}
```

---

**Sauvegarder le dashboard.**

---

**14. Exporter le dashboard en JSON (sauvegarde) :**

**Dashboard settings -> JSON Model**

**Copier tout le JSON.**

**Enregistrer dans un fichier local ou Git pour versionning.**

---

**15. Importer des dashboards communautaires (optionnel) :**

**Grafana a une bibliothèque de dashboards pré-faits.**

**Dashboards -> Import -> Import via grafana.com**

**Dashboards utiles :**

**Node Exporter Full :**
- Dashboard ID : `1860`
- Affiche CPU, RAM, disque, réseau détaillés

**Prometheus 2.0 Overview :**
- Dashboard ID : `3662`
- Métriques Prometheus lui-même

---

**Importer un dashboard :**

**Saisir l'ID : `1860`**

**Cliquer sur "Load"**

**Configuration :**

```
Name: Node Exporter Full
Folder: System
Prometheus: Prometheus (sélectionner la datasource)
```

**Cliquer sur "Import"**

**[OK] Dashboard Node Exporter importé !**

---

**16. Configurer l'auto-refresh :**

**Dans chaque dashboard, en haut à droite :**

**Time range picker -> Refresh interval -> 30s**

**Le dashboard se rafraîchira automatiquement toutes les 30 secondes.**

---

**17. Créer un dashboard "Aperçu Global" (Home) :**

**Nouveau dashboard avec des panels résumés :**

---

##### Row 1 : Statut général (Stats)

**4 panels Stat côte à côte :**

**Panel 1 : État Failover**
```promql
dhcp_failover_status{peer="dhcp-failover"}
```
**Value mapping : 1 -> NORMAL [OK], 0 -> DEGRADED [ATTENTION]**

---

**Panel 2 : Serveurs Actifs**
```promql
count(dhcp_server_status == 1)
```
**Compte combien de serveurs DHCP sont UP**

---

**Panel 3 : Utilisation Pool**
```promql
avg(dhcp_pool_utilization{pool="main"})
```
**Moyenne d'utilisation**

---

**Panel 4 : Baux Actifs Total**
```promql
sum(dhcp_active_leases{pool="main"})
```
**Somme de tous les baux actifs**

---

##### Row 2 : Graphiques (Time series)

**Panel 1 : Historique utilisation**
**Panel 2 : CPU serveurs**

---

##### Row 3 : Alertes actives (Alert list)

**Visualization : Alert list**

**Affiche les alertes Prometheus actives.**

**On configurera les alertes à l'ÉTAPE 8.**

---

**Sauvegarder :**

```
Name: DHCP - Aperçu Global
Folder: DHCP
```

---

**18. Définir ce dashboard comme Home :**

**Configuration (engrenage) -> Preferences**

**Home Dashboard :**

```
Sélectionner : DHCP - Aperçu Global
```

**Sauvegarder.**

**Maintenant, en ouvrant Grafana, on arrive directement sur ce dashboard.**

---

**19. Organiser les dashboards dans des dossiers :**

**Dashboards -> Manage**

**Créer des dossiers :**

```
DHCP
├─ DHCP - Aperçu Global
├─ DHCP Monitoring - Vue d'ensemble
├─ DHCP Failover - Détails
└─ DHCP Sécurité et Audit

System
├─ Node Exporter Full
└─ Prometheus 2.0 Overview
```

---

**20. Configurer les permissions (optionnel) :**

**Si plusieurs utilisateurs Grafana :**

**Configuration -> Users**

**Créer des rôles :**
- **Admin** : Peut tout modifier
- **Editor** : Peut créer/modifier des dashboards
- **Viewer** : Lecture seule

**Assigner les rôles aux utilisateurs.**

---

#### ÉTAPE 8 : Configurer Alertmanager et les alertes

**Alertmanager** = Composant de Prometheus pour gérer les alertes

**Fonctionnalités :**
- Déduplication des alertes
- Regroupement (grouping)
- Routage vers différents canaux (email, Slack, SMS, etc.)
- Silencing (mise en sourdine temporaire)

---

**Sur le serveur Prometheus :**

**1. Télécharger Alertmanager :**

```bash
cd /tmp
wget https://github.com/prometheus/alertmanager/releases/download/v0.26.0/alertmanager-0.26.0.linux-amd64.tar.gz
```

---

**2. Extraire :**

```bash
tar -xvzf alertmanager-0.26.0.linux-amd64.tar.gz
cd alertmanager-0.26.0.linux-amd64
```

---

**3. Copier les binaires :**

```bash
sudo cp alertmanager /usr/local/bin/
sudo cp amtool /usr/local/bin/
```

**Explication :**
- `alertmanager` : Serveur principal
- `amtool` : Outil CLI pour gérer les alertes

---

**4. Créer les répertoires :**

```bash
sudo mkdir -p /etc/alertmanager
sudo mkdir -p /var/lib/alertmanager
```

---

**5. Définir les permissions :**

```bash
sudo chown -R prometheus:prometheus /etc/alertmanager
sudo chown -R prometheus:prometheus /var/lib/alertmanager
sudo chown prometheus:prometheus /usr/local/bin/alertmanager
sudo chown prometheus:prometheus /usr/local/bin/amtool
```

---

**6. Créer la configuration Alertmanager :**

```bash
sudo nano /etc/alertmanager/alertmanager.yml
```

**Contenu :**

```yaml
# ═══════════════════════════════════════════════════════════════
# CONFIGURATION ALERTMANAGER
# ═══════════════════════════════════════════════════════════════

# ───────────────────────────────────────────────────────────────
# CONFIGURATION GLOBALE
# ───────────────────────────────────────────────────────────────

global:
  # Serveur SMTP pour les emails
  smtp_smarthost: 'smtp.gmail.com:587'
  smtp_from: 'alertes-dhcp@entreprise.local'
  smtp_auth_username: 'votre-email@gmail.com'
  smtp_auth_password: 'votre-mot-de-passe-app'
  
  # Explication :
  # smtp_smarthost = Serveur SMTP (format: host:port)
  # Gmail : smtp.gmail.com:587 (STARTTLS)
  # Autre : smtp.entreprise.local:25
  #
  # smtp_auth_password = Mot de passe d'application (pas le mot de passe Gmail)
  # Pour Gmail : Créer un mot de passe d'application dans les paramètres de sécurité
  #
  # [ATTENTION] En production, utiliser un serveur SMTP d'entreprise
  # Pas Gmail (limité à 500 emails/jour)
  
  # Résolveur externe (optionnel)
  # resolve_timeout: 5m
  
  # Slack (optionnel)
  # slack_api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'

# ───────────────────────────────────────────────────────────────
# TEMPLATES (MODÈLES D'ALERTE)
# ───────────────────────────────────────────────────────────────

templates:
  - '/etc/alertmanager/templates/*.tmpl'

# Explication :
# templates = Fichiers de templates pour personnaliser les messages
# On les créera plus tard

# ───────────────────────────────────────────────────────────────
# ROUTAGE DES ALERTES
# ───────────────────────────────────────────────────────────────

route:
  # Récepteur par défaut
  receiver: 'email-admin'
  
  # Explication :
  # receiver = Où envoyer les alertes par défaut
  # 'email-admin' = Défini plus bas dans receivers
  
  # Groupement des alertes
  group_by: ['alertname', 'instance']
  
  # Explication :
  # group_by = Grouper les alertes par ces labels
  # Évite de recevoir 10 emails séparés pour 10 instances
  # On reçoit 1 email avec les 10 alertes groupées
  #
  # Exemple :
  # Alerte "DiskFull" sur dhcp-primary et dhcp-secondary
  # Sans groupement : 2 emails
  # Avec group_by: ['alertname'] : 1 email avec les 2 instances
  
  # Délai avant le premier envoi d'alerte
  group_wait: 30s
  
  # Explication :
  # group_wait = Attendre X secondes avant d'envoyer la première alerte
  # Permet d'accumuler les alertes du même groupe
  # 30s = Attendre 30 secondes pour voir s'il y a d'autres alertes similaires
  
  # Délai entre deux envois pour le même groupe
  group_interval: 5m
  
  # Explication :
  # group_interval = Délai entre deux notifications du même groupe
  # Si de nouvelles alertes arrivent dans le même groupe,
  # elles seront envoyées après 5 minutes (pas immédiatement)
  
  # Délai avant de renvoyer la même alerte
  repeat_interval: 12h
  
  # Explication :
  # repeat_interval = Fréquence de rappel pour une alerte persistante
  # Si l'alerte n'est pas résolue, rappel toutes les 12 heures
  # Évite le spam si un problème dure longtemps
  
  # ─────────────────────────────────────────────────────────────
  # ROUTES SPÉCIFIQUES
  # ─────────────────────────────────────────────────────────────
  
  routes:
    # Route 1 : Alertes CRITIQUES -> Email + Slack
    - match:
        severity: critical
      receiver: 'critical-alerts'
      group_wait: 10s
      repeat_interval: 1h
      
      # Explication :
      # match = Filtre sur les labels
      # severity: critical = Seulement les alertes critiques
      # receiver = Récepteur spécifique (défini plus bas)
      # group_wait: 10s = Envoyer plus rapidement (10s au lieu de 30s)
      # repeat_interval: 1h = Rappeler plus souvent (1h au lieu de 12h)
    
    # Route 2 : Alertes DHCP -> Email admin DHCP
    - match_re:
        alertname: '^DHCP.*'
      receiver: 'dhcp-admin'
      
      # Explication :
      # match_re = Filtre avec regex
      # alertname: '^DHCP.*' = Toutes les alertes commençant par "DHCP"
      # receiver: 'dhcp-admin' = Admin DHCP uniquement
    
    # Route 3 : Alertes Failover -> Email + SMS
    - match:
        alertname: 'DHCPFailoverDown'
      receiver: 'failover-critical'
      group_wait: 5s
      repeat_interval: 30m

# ───────────────────────────────────────────────────────────────
# INHIBITION (SUPPRESSION DE DOUBLONS)
# ───────────────────────────────────────────────────────────────

inhibit_rules:
  # Règle 1 : Si serveur down, ne pas alerter sur ses métriques
  - source_match:
      alertname: 'DHCPServerDown'
    target_match_re:
      alertname: 'DHCP.*'
    equal: ['instance']
  
  # Explication :
  # inhibit_rules = Règles de suppression d'alertes
  #
  # Si DHCPServerDown est actif sur "dhcp-primary",
  # toutes les autres alertes DHCP sur "dhcp-primary" sont supprimées
  #
  # Évite le spam : pas besoin de recevoir "PoolFull" + "CPUHigh" + "MemoryFull"
  # si le serveur est totalement down
  #
  # source_match = Alerte source (celle qui inhibe)
  # target_match_re = Alertes cibles (celles qui sont inhibées)
  # equal = Doit matcher sur ces labels

# ───────────────────────────────────────────────────────────────
# RÉCEPTEURS (DESTINATIONS DES ALERTES)
# ───────────────────────────────────────────────────────────────

receivers:
  # ─────────────────────────────────────────────────────────────
  # Récepteur 1 : Email admin général
  # ─────────────────────────────────────────────────────────────
  
  - name: 'email-admin'
    email_configs:
      - to: 'admin@entreprise.local'
        send_resolved: true
        
        # Explication :
        # to = Destinataire
        # send_resolved: true = Envoyer aussi quand l'alerte est résolue
        
        # Template du sujet
        headers:
          Subject: '[Alerte DHCP] {{ .GroupLabels.alertname }}'
        
        # Template du corps (HTML)
        html: |
          <h2>Alerte DHCP</h2>
          <p><strong>Nom :</strong> {{ .GroupLabels.alertname }}</p>
          <p><strong>Sévérité :</strong> {{ .CommonLabels.severity }}</p>
          <p><strong>Instance :</strong> {{ .CommonLabels.instance }}</p>
          <p><strong>Description :</strong> {{ .CommonAnnotations.description }}</p>
          <p><strong>Date :</strong> {{ .StartsAt }}</p>
  
  # ─────────────────────────────────────────────────────────────
  # Récepteur 2 : Alertes critiques (Email + Slack)
  # ─────────────────────────────────────────────────────────────
  
  - name: 'critical-alerts'
    email_configs:
      - to: 'admin@entreprise.local, cto@entreprise.local'
        send_resolved: true
    
    # Slack (décommenter si configuré)
    # slack_configs:
    #   - api_url: 'https://hooks.slack.com/services/YOUR/WEBHOOK/URL'
    #     channel: '#alertes-critiques'
    #     title: 'Alerte DHCP Critique'
    #     text: '{{ .CommonAnnotations.description }}'
    #     send_resolved: true
  
  # ─────────────────────────────────────────────────────────────
  # Récepteur 3 : Admin DHCP spécifique
  # ─────────────────────────────────────────────────────────────
  
  - name: 'dhcp-admin'
    email_configs:
      - to: 'dhcp-admin@entreprise.local'
        send_resolved: true
  
  # ─────────────────────────────────────────────────────────────
  # Récepteur 4 : Failover critique (Email + SMS via webhook)
  # ─────────────────────────────────────────────────────────────
  
  - name: 'failover-critical'
    email_configs:
      - to: 'admin@entreprise.local'
        send_resolved: true
    
    # Webhook pour SMS (exemple avec service externe)
    # webhook_configs:
    #   - url: 'https://api.sms-service.com/send'
    #     send_resolved: false

# ═══════════════════════════════════════════════════════════════
```

**Sauvegarder.**

---

**7. Créer le répertoire pour les templates :**

```bash
sudo mkdir -p /etc/alertmanager/templates
```

---

**8. Créer un template d'email personnalisé (optionnel) :**

```bash
sudo nano /etc/alertmanager/templates/email.tmpl
```

**Contenu :**

```go
{{ define "email.default.subject" }}
[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }}
{{ end }}

{{ define "email.default.html" }}
<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: Arial, sans-serif; }
    .alert { padding: 10px; margin: 10px 0; border-radius: 5px; }
    .firing { background-color: #f8d7da; border: 1px solid #f5c6cb; }
    .resolved { background-color: #d4edda; border: 1px solid #c3e6cb; }
  </style>
</head>
<body>
  <h2>Alertes DHCP</h2>
  
  {{ if gt (len .Alerts.Firing) 0 }}
  <h3>[HOT] Alertes actives ({{ .Alerts.Firing | len }})</h3>
  {{ range .Alerts.Firing }}
  <div class="alert firing">
    <strong>{{ .Labels.alertname }}</strong><br>
    <strong>Instance:</strong> {{ .Labels.instance }}<br>
    <strong>Sévérité:</strong> {{ .Labels.severity }}<br>
    <strong>Description:</strong> {{ .Annotations.description }}<br>
    <strong>Début:</strong> {{ .StartsAt }}<br>
  </div>
  {{ end }}
  {{ end }}
  
  {{ if gt (len .Alerts.Resolved) 0 }}
  <h3>[OK] Alertes résolues ({{ .Alerts.Resolved | len }})</h3>
  {{ range .Alerts.Resolved }}
  <div class="alert resolved">
    <strong>{{ .Labels.alertname }}</strong><br>
    <strong>Instance:</strong> {{ .Labels.instance }}<br>
    <strong>Résolu à:</strong> {{ .EndsAt }}<br>
  </div>
  {{ end }}
  {{ end }}
</body>
</html>
{{ end }}
```

**Sauvegarder.**

---

**9. Vérifier la configuration :**

```bash
amtool check-config /etc/alertmanager/alertmanager.yml
```

**Résultat attendu :**

```
Checking '/etc/alertmanager/alertmanager.yml'  SUCCESS
Found:
 - global config
 - route
 - 4 inhibit rules
 - 4 receivers
```

**[OK] Configuration valide !**

---

**10. Créer le service systemd :**

```bash
sudo nano /etc/systemd/system/alertmanager.service
```

**Contenu :**

```ini
[Unit]
Description=Alertmanager
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=prometheus
Group=prometheus

ExecStart=/usr/local/bin/alertmanager \
  --config.file=/etc/alertmanager/alertmanager.yml \
  --storage.path=/var/lib/alertmanager/ \
  --web.listen-address=:9093 \
  --cluster.listen-address=

# Explication :
# --config.file = Fichier de configuration
# --storage.path = Stockage des données (silences, etc.)
# --web.listen-address=:9093 = Port de l'interface web
# --cluster.listen-address= = Désactiver le clustering (1 seul Alertmanager)

Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
```

**Sauvegarder.**

---

**11. Démarrer Alertmanager :**

```bash
sudo systemctl daemon-reload
sudo systemctl enable alertmanager
sudo systemctl start alertmanager
```

**Vérifier :**

```bash
sudo systemctl status alertmanager
```

**Résultat attendu : `Active: active (running)`**

---

**12. Autoriser le port 9093 :**

```bash
sudo ufw allow 9093/tcp comment 'Alertmanager Web UI'
```

---

**13. Accéder à l'interface Alertmanager :**

**Ouvrir un navigateur : `http://192.168.10.10:9093`**

**Interface Alertmanager :**

```
┌─────────────────────────────────────────────────────────────┐
│ Alertmanager                              [Alerts] [Silences]│
├─────────────────────────────────────────────────────────────┤
│ No alerts found                                             │
└─────────────────────────────────────────────────────────────┘
```

**Normal : aucune alerte pour l'instant.**

---

**14. Créer les règles d'alerte Prometheus :**

```bash
sudo nano /etc/prometheus/rules/dhcp_alerts.yml
```

**Contenu :**

```yaml
# ═══════════════════════════════════════════════════════════════
# RÈGLES D'ALERTE DHCP POUR PROMETHEUS
# ═══════════════════════════════════════════════════════════════

groups:
  - name: dhcp_alerts
    interval: 30s
    
    rules:
      # ─────────────────────────────────────────────────────────
      # Alerte 1 : Pool DHCP > 80% d'utilisation
      # ─────────────────────────────────────────────────────────
      
      - alert: DHCPPoolHighUtilization
        expr: dhcp_pool_utilization{pool="main"} > 80
        for: 5m
        labels:
          severity: warning
          component: dhcp
        annotations:
          summary: "Pool DHCP en forte utilisation"
          description: "Le pool DHCP {{ $labels.pool }} sur {{ $labels.instance }} est utilisé à {{ $value | humanizePercentage }}."
        
        # Explication :
        # alert = Nom de l'alerte
        # expr = Expression PromQL (condition de déclenchement)
        # for = Durée pendant laquelle la condition doit être vraie
        #       Évite les faux positifs (pic temporaire)
        # labels = Labels ajoutés à l'alerte
        # annotations = Informations descriptives
        #
        # {{ $labels.pool }} = Valeur du label "pool"
        # {{ $value }} = Valeur de la métrique
        # humanizePercentage = Filtre pour afficher en pourcentage
      
      # ─────────────────────────────────────────────────────────
      # Alerte 2 : Pool DHCP > 90% (CRITIQUE)
      # ─────────────────────────────────────────────────────────
      
      - alert: DHCPPoolCriticalUtilization
        expr: dhcp_pool_utilization{pool="main"} > 90
        for: 2m
        labels:
          severity: critical
          component: dhcp
        annotations:
          summary: "Pool DHCP presque épuisé !"
          description: "CRITIQUE : Le pool DHCP {{ $labels.pool }} sur {{ $labels.instance }} est utilisé à {{ $value | humanizePercentage }}. Plus que {{ query \"dhcp_pool_available{pool='main', instance='\" + $labels.instance + \"'}\" | first | value }} adresses disponibles."
      
      # ─────────────────────────────────────────────────────────
      # Alerte 3 : Serveur DHCP down
      # ─────────────────────────────────────────────────────────
      
      - alert: DHCPServerDown
        expr: dhcp_server_status == 0
        for: 1m
        labels:
          severity: critical
          component: dhcp
        annotations:
          summary: "Serveur DHCP hors service"
          description: "Le serveur DHCP {{ $labels.instance }} est arrêté."
      
      # ─────────────────────────────────────────────────────────
      # Alerte 4 : Failover pas en mode NORMAL
      # ─────────────────────────────────────────────────────────
      
      - alert: DHCPFailoverNotNormal
        expr: dhcp_failover_status{peer="dhcp-failover"} != 1
        for: 2m
        labels:
          severity: warning
          component: dhcp-failover
        annotations:
          summary: "Failover DHCP en mode dégradé"
          description: "Le failover DHCP {{ $labels.peer }} sur {{ $labels.instance }} n'est PAS en mode NORMAL."
      
      # ─────────────────────────────────────────────────────────
      # Alerte 5 : Partner DOWN
      # ─────────────────────────────────────────────────────────
      
      - alert: DHCPFailoverPartnerDown
        expr: dhcp_failover_local_state{peer="dhcp-failover", state_name="PARTNER-DOWN"} == 4
        for: 1m
        labels:
          severity: critical
          component: dhcp-failover
        annotations:
          summary: "Serveur DHCP partenaire tombé"
          description: "CRITIQUE : Le serveur {{ $labels.instance }} a détecté que son partenaire failover est DOWN. Le service DHCP fonctionne en mode dégradé (un seul serveur)."
      
      # ─────────────────────────────────────────────────────────
      # Alerte 6 : CPU élevé sur serveur DHCP
      # ─────────────────────────────────────────────────────────
      
      - alert: DHCPServerHighCPU
        expr: 100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle", instance=~"dhcp.*"}[5m])) * 100) > 80
        for: 5m
        labels:
          severity: warning
          component: system
        annotations:
          summary: "CPU élevé sur serveur DHCP"
          description: "Le serveur {{ $labels.instance }} a un CPU élevé : {{ $value | humanize }}%."
      
      # ─────────────────────────────────────────────────────────
      # Alerte 7 : Mémoire élevée
      # ─────────────────────────────────────────────────────────
      
      - alert: DHCPServerHighMemory
        expr: 100 * (1 - ((node_memory_MemAvailable_bytes{instance=~"dhcp.*"} or node_memory_MemFree_bytes{instance=~"dhcp.*"}) / node_memory_MemTotal_bytes{instance=~"dhcp.*"})) > 85
        for: 5m
        labels:
          severity: warning
          component: system
        annotations:
          summary: "Mémoire élevée sur serveur DHCP"
          description: "Le serveur {{ $labels.instance }} utilise {{ $value | humanizePercentage }} de sa mémoire."
      
      # ─────────────────────────────────────────────────────────
      # Alerte 8 : Disque presque plein
      # ─────────────────────────────────────────────────────────
      
      - alert: DHCPServerDiskFull
        expr: 100 - ((node_filesystem_avail_bytes{instance=~"dhcp.*", mountpoint="/"} / node_filesystem_size_bytes{instance=~"dhcp.*", mountpoint="/"}) * 100) > 85
        for: 5m
        labels:
          severity: warning
          component: system
        annotations:
          summary: "Disque presque plein"
          description: "Le disque / du serveur {{ $labels.instance }} est utilisé à {{ $value | humanizePercentage }}."

# ═══════════════════════════════════════════════════════════════
```

**Sauvegarder.**

---

**15. Vérifier la syntaxe des règles :**

```bash
promtool check rules /etc/prometheus/rules/dhcp_alerts.yml
```

**Résultat attendu :**

```
Checking /etc/prometheus/rules/dhcp_alerts.yml
  SUCCESS: 8 rules found
```

**[OK] Règles valides !**

---

**16. Recharger Prometheus :**

```bash
# Envoyer le signal HUP pour recharger la config
sudo killall -HUP prometheus
```

**Ou redémarrer :**

```bash
sudo systemctl restart prometheus
```

---

**17. Vérifier que les règles sont chargées :**

**Dans Prometheus UI : Status -> Rules**

**Devrait afficher les 8 alertes.**

---

**18. Tester une alerte (simulation) :**

**Simuler un pool > 80% :**

**Créer beaucoup de clients DHCP pour saturer le pool.**

**Ou modifier temporairement la règle pour tester :**

```yaml
- alert: DHCPPoolHighUtilization
  expr: dhcp_pool_utilization{pool="main"} > 10  # Seuil bas pour test
  for: 30s  # Durée courte pour test
```

**Recharger Prometheus.**

**Après 30 secondes, l'alerte devrait se déclencher.**

---

**19. Vérifier dans Alertmanager :**

**Aller sur `http://192.168.10.10:9093`**

**L'alerte devrait apparaître dans la section "Firing".**

---

**20. Vérifier la réception d'email :**

**Si la configuration SMTP est correcte, un email devrait être reçu.**

---

**21. Créer des silences (mise en sourdine) :**

**Dans Alertmanager UI : Silences -> New Silence**

**Exemple : Maintenance programmée**

```
Matchers:
  instance = dhcp-secondary

Starts: 2024-12-17 04:00:00
Ends: 2024-12-17 06:00:00

Creator: admin
Comment: Maintenance serveur secondary (mise à jour système)
```

**Cliquer sur "Create"**

**Pendant cette période, aucune alerte pour `dhcp-secondary` ne sera envoyée.**

---

**22. Intégrer Alertmanager dans Grafana :**

**Dans Grafana : Alerting -> Contact points -> Add contact point**

**Configuration :**

```
Name: Alertmanager
Type: Alertmanager

URL: http://localhost:9093
Basic auth: Non
```

**Cliquer sur "Test" puis "Save contact point"**

**[OK] Alertmanager intégré à Grafana !**

---

**Félicitations ! La stack complète de monitoring et d'alerting est opérationnelle ! [BRAVO]**

---

Je continue avec la PARTIE 3 : OPTIMISATION ET RAPPORTS (ÉTAPE 9) ?