Browse Source

Add WPA2-Enterprise PEAP support and update configuration UI

Support-PEAP
scayac 1 month ago
parent
commit
3b095016fe
  1. 47
      README.md
  2. 60
      data/settings.html
  3. 101
      src/main.cpp
  4. 12
      src/server.cpp

47
README.md

@ -7,6 +7,7 @@ Squelette d'application web pour ESP32 avec interface de gestion via dashboard w
- **Dashboard web responsive** - Interface utilisateur moderne et adaptative - **Dashboard web responsive** - Interface utilisateur moderne et adaptative
- **Authentification sécurisée** - Système de login avec sessions et hash SHA-256 - **Authentification sécurisée** - Système de login avec sessions et hash SHA-256
- **Configuration WiFi** - Mode Station et Point d'Accès automatique - **Configuration WiFi** - Mode Station et Point d'Accès automatique
- **WPA2-Enterprise PEAP** - Connexion PEAP (sans certificat) avec utilisateur/mot de passe (MSCHAPv2)
- **API REST** - Endpoints pour configuration, statistiques et changement de mot de passe - **API REST** - Endpoints pour configuration, statistiques et changement de mot de passe
- **Gestion des mots de passe** - Changement sécurisé avec validation de l'ancien mot de passe - **Gestion des mots de passe** - Changement sécurisé avec validation de l'ancien mot de passe
- **Mise à jour OTA** - Mise à jour du firmware et système de fichiers via web avec modales de confirmation - **Mise à jour OTA** - Mise à jour du firmware et système de fichiers via web avec modales de confirmation
@ -69,7 +70,9 @@ platformio device monitor --environment esp32
1. Accédez à la page **Paramètres** 1. Accédez à la page **Paramètres**
2. Section **Configuration WiFi** 2. Section **Configuration WiFi**
3. Entrez le SSID et le mot de passe de votre réseau 3. Choisissez le type de sécurité :
- **WPA2-PSK** (classique) : SSID + mot de passe WiFi
- **WPA2-Enterprise PEAP** : SSID + utilisateur + mot de passe (sans certificat)
4. Cliquez sur **Enregistrer WiFi** 4. Cliquez sur **Enregistrer WiFi**
5. L'ESP32 redémarre et se connecte à votre réseau 5. L'ESP32 redémarre et se connecte à votre réseau
@ -176,7 +179,10 @@ Tous les assets CSS, JS et HTML sont compressés automatiquement en gzip avant l
}, },
"wifi": { "wifi": {
"ssid": "Nom_du_réseau", "ssid": "Nom_du_réseau",
"security": "wpa2",
"password": "mot_de_passe_wifi", "password": "mot_de_passe_wifi",
"eap_username": "utilisateur_peap",
"eap_password": "mot_de_passe_peap",
"ap_password": "webapp123" "ap_password": "webapp123"
}, },
"system": { "system": {
@ -189,6 +195,44 @@ Tous les assets CSS, JS et HTML sont compressés automatiquement en gzip avant l
} }
``` ```
### Exemple sécurisé prêt à copier (WPA2-Enterprise PEAP)
```json
{
"auth": {
"username": "admin",
"password_hash": "hash_sha256_du_mot_de_passe"
},
"wifi": {
"ssid": "Mon-SSID-Entreprise",
"security": "wpa2_peap",
"password": "",
"eap_username": "prenom.nom",
"eap_password": "",
"ap_password": "webapp123"
},
"system": {
"hostname": "ESP32-Webapp",
"session_timeout": 60
},
"params": {
"app_name": "ESP32 Webapp"
}
}
```
Notes:
- Aucun certificat n'est requis pour ce mode PEAP.
- Le mode PEAP est configuré en auto avec authentification interne MSCHAPv2.
- Remplacez `eap_username` par votre identifiant entreprise.
- Laissez `eap_password` vide dans le fichier, puis saisissez-le uniquement dans l'interface web.
Procédure recommandée (sans secret en clair dans les fichiers):
1. Téléversez cette configuration avec `eap_password` vide.
2. Ouvrez la page Paramètres de l'ESP32.
3. Sélectionnez `WPA2-Enterprise PEAP`, saisissez l'utilisateur et le mot de passe PEAP.
4. Enregistrez : l'ESP32 redémarre et se connecte.
## 🛠 Dépendances ## 🛠 Dépendances
- **ESPAsyncWebServer** - Serveur web asynchrone - **ESPAsyncWebServer** - Serveur web asynchrone
@ -204,6 +248,7 @@ Tous les assets CSS, JS et HTML sont compressés automatiquement en gzip avant l
### L'ESP32 ne se connecte pas au WiFi ### L'ESP32 ne se connecte pas au WiFi
1. Vérifiez le SSID et le mot de passe dans la configuration 1. Vérifiez le SSID et le mot de passe dans la configuration
2. En mode WPA2-PEAP, vérifiez l'utilisateur/mot de passe PEAP
2. Assurez-vous que le réseau WiFi est en 2.4 GHz (l'ESP32 ne supporte pas le 5 GHz) 2. Assurez-vous que le réseau WiFi est en 2.4 GHz (l'ESP32 ne supporte pas le 5 GHz)
3. L'ESP32 passera automatiquement en mode AP si la connexion échoue 3. L'ESP32 passera automatiquement en mode AP si la connexion échoue

60
data/settings.html

@ -23,16 +23,34 @@
<div class="card"> <div class="card">
<h2>Configuration WiFi</h2> <h2>Configuration WiFi</h2>
<form id="wifiForm"> <form id="wifiForm">
<div class="form-group">
<label for="wifi_security">Type de sécurité</label>
<select id="wifi_security" name="wifi_security">
<option value="wpa2">WPA2-PSK (classique)</option>
<option value="wpa2_peap">WPA2-Enterprise PEAP (MSCHAPv2)</option>
</select>
</div>
<div class="form-group"> <div class="form-group">
<label for="wifi_ssid">SSID du réseau</label> <label for="wifi_ssid">SSID du réseau</label>
<input type="text" id="wifi_ssid" name="wifi_ssid"> <input type="text" id="wifi_ssid" name="wifi_ssid">
</div> </div>
<div class="form-group"> <div class="form-group" id="wifi_password_group">
<label for="wifi_password">Mot de passe WiFi</label> <label for="wifi_password">Mot de passe WiFi</label>
<input type="password" id="wifi_password" name="wifi_password" placeholder="Laisser vide pour ne pas changer"> <input type="password" id="wifi_password" name="wifi_password" placeholder="Laisser vide pour ne pas changer">
</div> </div>
<div class="form-group" id="eap_username_group" style="display: none;">
<label for="eap_username">Utilisateur PEAP</label>
<input type="text" id="eap_username" name="eap_username" placeholder="Nom d'utilisateur" autocomplete="username">
</div>
<div class="form-group" id="eap_password_group" style="display: none;">
<label for="eap_password">Mot de passe PEAP</label>
<input type="password" id="eap_password" name="eap_password" placeholder="Laisser vide pour ne pas changer" autocomplete="current-password">
</div>
<div class="form-group"> <div class="form-group">
<label for="ap_password">Mot de passe du Point d'Accès</label> <label for="ap_password">Mot de passe du Point d'Accès</label>
<input type="password" id="ap_password" name="ap_password" placeholder="Laisser vide pour ne pas changer"> <input type="password" id="ap_password" name="ap_password" placeholder="Laisser vide pour ne pas changer">
@ -89,12 +107,23 @@
<script> <script>
let config = {}; let config = {};
function toggleWifiSecurityFields() {
const security = document.getElementById('wifi_security').value;
const isPeap = security === 'wpa2_peap';
document.getElementById('wifi_password_group').style.display = isPeap ? 'none' : 'block';
document.getElementById('eap_username_group').style.display = isPeap ? 'block' : 'none';
document.getElementById('eap_password_group').style.display = isPeap ? 'block' : 'none';
}
async function loadConfig() { async function loadConfig() {
try { try {
config = await api.getConfig(); config = await api.getConfig();
// WiFi // WiFi
document.getElementById('wifi_security').value = config.wifi.security || 'wpa2';
document.getElementById('wifi_ssid').value = config.wifi.ssid || ''; document.getElementById('wifi_ssid').value = config.wifi.ssid || '';
document.getElementById('eap_username').value = config.wifi.eap_username || '';
toggleWifiSecurityFields();
// Système // Système
document.getElementById('hostname').value = config.system.hostname || ''; document.getElementById('hostname').value = config.system.hostname || '';
@ -117,11 +146,32 @@
document.getElementById('wifiForm').addEventListener('submit', async (e) => { document.getElementById('wifiForm').addEventListener('submit', async (e) => {
e.preventDefault(); e.preventDefault();
config.wifi.security = document.getElementById('wifi_security').value;
config.wifi.ssid = document.getElementById('wifi_ssid').value; config.wifi.ssid = document.getElementById('wifi_ssid').value;
const wifiPassword = document.getElementById('wifi_password').value; if (config.wifi.security === 'wpa2_peap') {
if (wifiPassword) { const eapUsername = document.getElementById('eap_username').value.trim();
config.wifi.password = wifiPassword; if (!eapUsername) {
showMessage('Le nom d\'utilisateur PEAP est requis', 'error');
return;
}
config.wifi.eap_username = eapUsername;
const eapPassword = document.getElementById('eap_password').value;
if (eapPassword) {
config.wifi.eap_password = eapPassword;
}
config.wifi.password = '';
} else {
const wifiPassword = document.getElementById('wifi_password').value;
if (wifiPassword) {
config.wifi.password = wifiPassword;
}
config.wifi.eap_username = '';
config.wifi.eap_password = '';
} }
const apPassword = document.getElementById('ap_password').value; const apPassword = document.getElementById('ap_password').value;
@ -137,6 +187,8 @@
} }
}); });
document.getElementById('wifi_security').addEventListener('change', toggleWifiSecurityFields);
document.getElementById('systemForm').addEventListener('submit', async (e) => { document.getElementById('systemForm').addEventListener('submit', async (e) => {
e.preventDefault(); e.preventDefault();

101
src/main.cpp

@ -3,6 +3,13 @@
#include <LittleFS.h> #include <LittleFS.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <ArduinoOTA.h> #include <ArduinoOTA.h>
#if __has_include(<esp_wpa2.h>)
#include <esp_wpa2.h>
#include <esp_wifi.h>
#define HAS_LEGACY_WPA2_ENTERPRISE 1
#else
#define HAS_LEGACY_WPA2_ENTERPRISE 0
#endif
#include "auth.h" #include "auth.h"
#include "server.h" #include "server.h"
#include "utils.h" #include "utils.h"
@ -11,6 +18,9 @@
String hostname = "ESP32-Webapp"; String hostname = "ESP32-Webapp";
String wifiSSID = ""; String wifiSSID = "";
String wifiPassword = ""; String wifiPassword = "";
String wifiSecurity = "wpa2";
String wifiEapUsername = "";
String wifiEapPassword = "";
String apPassword = "webapp123"; String apPassword = "webapp123";
void createDefaultConfig() { void createDefaultConfig() {
@ -25,6 +35,9 @@ void createDefaultConfig() {
// Configuration WiFi // Configuration WiFi
doc["wifi"]["ssid"] = ""; doc["wifi"]["ssid"] = "";
doc["wifi"]["password"] = ""; doc["wifi"]["password"] = "";
doc["wifi"]["security"] = "wpa2";
doc["wifi"]["eap_username"] = "";
doc["wifi"]["eap_password"] = "";
doc["wifi"]["ap_password"] = apPassword; doc["wifi"]["ap_password"] = apPassword;
// Configuration système // Configuration système
@ -90,6 +103,18 @@ void loadConfig() {
wifiPassword = doc["wifi"]["password"].as<String>(); wifiPassword = doc["wifi"]["password"].as<String>();
} }
if (doc["wifi"]["security"]) {
wifiSecurity = doc["wifi"]["security"].as<String>();
}
if (doc["wifi"]["eap_username"]) {
wifiEapUsername = doc["wifi"]["eap_username"].as<String>();
}
if (doc["wifi"]["eap_password"]) {
wifiEapPassword = doc["wifi"]["eap_password"].as<String>();
}
if (doc["wifi"]["ap_password"]) { if (doc["wifi"]["ap_password"]) {
apPassword = doc["wifi"]["ap_password"].as<String>(); apPassword = doc["wifi"]["ap_password"].as<String>();
} }
@ -97,23 +122,79 @@ void loadConfig() {
logMessage(LOG_INFO, "Configuration chargée"); logMessage(LOG_INFO, "Configuration chargée");
} }
bool waitForWiFiConnection() {
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
logMessage(LOG_INFO, "WiFi connecté");
logMessage(LOG_INFO, "Adresse IP: " + WiFi.localIP().toString());
return true;
}
return false;
}
bool connectWPA2PEAP() {
if (wifiEapUsername.isEmpty() || wifiEapPassword.isEmpty()) {
logMessage(LOG_WARN, "WPA2-PEAP: identifiant ou mot de passe manquant");
return false;
}
logMessage(LOG_INFO, "Connexion WPA2-PEAP au WiFi: " + wifiSSID);
#if defined(WPA2_AUTH_PEAP)
WiFi.begin(
wifiSSID.c_str(),
WPA2_AUTH_PEAP,
wifiEapUsername.c_str(),
wifiEapUsername.c_str(),
wifiEapPassword.c_str(),
nullptr
);
return waitForWiFiConnection();
#elif HAS_LEGACY_WPA2_ENTERPRISE
WiFi.begin(wifiSSID.c_str());
esp_wifi_sta_wpa2_ent_set_identity((uint8_t*)wifiEapUsername.c_str(), wifiEapUsername.length());
esp_wifi_sta_wpa2_ent_set_username((uint8_t*)wifiEapUsername.c_str(), wifiEapUsername.length());
esp_wifi_sta_wpa2_ent_set_password((uint8_t*)wifiEapPassword.c_str(), wifiEapPassword.length());
#ifdef ESP_EAP_TTLS_PHASE2_MSCHAPV2
esp_wifi_sta_wpa2_ent_set_ttls_phase2_method(ESP_EAP_TTLS_PHASE2_MSCHAPV2);
#endif
esp_wifi_sta_wpa2_ent_enable();
return waitForWiFiConnection();
#else
logMessage(LOG_ERROR, "WPA2-PEAP non supporté par cette version du framework");
return false;
#endif
}
bool connectWPA2PSK() {
logMessage(LOG_INFO, "Connexion WPA2-PSK au WiFi: " + wifiSSID);
WiFi.begin(wifiSSID.c_str(), wifiPassword.c_str());
return waitForWiFiConnection();
}
void setupWiFi() { void setupWiFi() {
if (wifiSSID.length() > 0) { if (wifiSSID.length() > 0) {
logMessage(LOG_INFO, "Connexion au WiFi: " + wifiSSID);
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
WiFi.setHostname(hostname.c_str()); WiFi.setHostname(hostname.c_str());
WiFi.begin(wifiSSID.c_str(), wifiPassword.c_str());
int attempts = 0; bool connected = false;
while (WiFi.status() != WL_CONNECTED && attempts < 20) { if (wifiSecurity == "wpa2_peap") {
delay(500); connected = connectWPA2PEAP();
Serial.print("."); } else {
attempts++; connected = connectWPA2PSK();
} }
if (WiFi.status() == WL_CONNECTED) { if (connected) {
logMessage(LOG_INFO, "WiFi connecté");
logMessage(LOG_INFO, "Adresse IP: " + WiFi.localIP().toString());
return; return;
} }

12
src/server.cpp

@ -183,6 +183,9 @@ void WebServerManager::handleGetConfig(AsyncWebServerRequest* request) {
if (doc["wifi"]["password"]) { if (doc["wifi"]["password"]) {
doc["wifi"]["password"] = "***"; doc["wifi"]["password"] = "***";
} }
if (doc["wifi"]["eap_password"]) {
doc["wifi"]["eap_password"] = "***";
}
if (doc["wifi"]["ap_password"]) { if (doc["wifi"]["ap_password"]) {
doc["wifi"]["ap_password"] = "***"; doc["wifi"]["ap_password"] = "***";
} }
@ -232,6 +235,9 @@ void WebServerManager::handleSetConfig(AsyncWebServerRequest* request) {
if (newDoc["wifi"]["password"] && newDoc["wifi"]["password"] != "***") { if (newDoc["wifi"]["password"] && newDoc["wifi"]["password"] != "***") {
existingDoc["wifi"]["password"] = newDoc["wifi"]["password"]; existingDoc["wifi"]["password"] = newDoc["wifi"]["password"];
} }
if (!newDoc["wifi"]["eap_password"].isNull() && newDoc["wifi"]["eap_password"] != "***") {
existingDoc["wifi"]["eap_password"] = newDoc["wifi"]["eap_password"];
}
if (newDoc["wifi"]["ap_password"] && newDoc["wifi"]["ap_password"] != "***") { if (newDoc["wifi"]["ap_password"] && newDoc["wifi"]["ap_password"] != "***") {
existingDoc["wifi"]["ap_password"] = newDoc["wifi"]["ap_password"]; existingDoc["wifi"]["ap_password"] = newDoc["wifi"]["ap_password"];
} }
@ -246,6 +252,12 @@ void WebServerManager::handleSetConfig(AsyncWebServerRequest* request) {
if (newDoc["wifi"]["ssid"]) { if (newDoc["wifi"]["ssid"]) {
existingDoc["wifi"]["ssid"] = newDoc["wifi"]["ssid"]; existingDoc["wifi"]["ssid"] = newDoc["wifi"]["ssid"];
} }
if (!newDoc["wifi"]["security"].isNull()) {
existingDoc["wifi"]["security"] = newDoc["wifi"]["security"];
}
if (!newDoc["wifi"]["eap_username"].isNull()) {
existingDoc["wifi"]["eap_username"] = newDoc["wifi"]["eap_username"];
}
if (newDoc["system"]["hostname"]) { if (newDoc["system"]["hostname"]) {
existingDoc["system"]["hostname"] = newDoc["system"]["hostname"]; existingDoc["system"]["hostname"] = newDoc["system"]["hostname"];
} }

Loading…
Cancel
Save