Fix: avoid deprecated setAuthentication() to fix memory exhaustion

with ESPAsyncWebServer 3.3.0, the setAuthentication() method became
deprecated and a replacement method was provided which acts as a shim
and uses the new middleware-based approach to setup authentication. in
order to eventually apply a changed "read-only access allowed" setting,
the setAuthentication() method was called periodically. the shim
implementation each time allocates a new AuthenticationMiddleware and
adds it to the chain of middlewares, eventually exhausting the memory.

we now use the new middleware-based approach ourselves and only add the
respective AuthenticatonMiddleware instance once to the respective
websocket server instance.

a regression where enabling unauthenticated read-only access is not
applied until reboot is also fixed. all the AuthenticationMiddleware
instances were never removed from the chain of middlewares when calling
setAuthentication("", "").
This commit is contained in:
Bernhard Kirchen 2024-09-30 14:18:33 +02:00
parent b206cee820
commit ebb225f6c0
7 changed files with 46 additions and 12 deletions

View File

@ -30,6 +30,7 @@ class WebApiClass {
public: public:
WebApiClass(); WebApiClass();
void init(Scheduler& scheduler); void init(Scheduler& scheduler);
void reload();
static bool checkCredentials(AsyncWebServerRequest* request); static bool checkCredentials(AsyncWebServerRequest* request);
static bool checkCredentialsReadonly(AsyncWebServerRequest* request); static bool checkCredentialsReadonly(AsyncWebServerRequest* request);

View File

@ -8,9 +8,11 @@ class WebApiWsConsoleClass {
public: public:
WebApiWsConsoleClass(); WebApiWsConsoleClass();
void init(AsyncWebServer& server, Scheduler& scheduler); void init(AsyncWebServer& server, Scheduler& scheduler);
void reload();
private: private:
AsyncWebSocket _ws; AsyncWebSocket _ws;
AuthenticationMiddleware _simpleDigestAuth;
Task _wsCleanupTask; Task _wsCleanupTask;
void wsCleanupTaskCb(); void wsCleanupTaskCb();

View File

@ -11,6 +11,7 @@ class WebApiWsLiveClass {
public: public:
WebApiWsLiveClass(); WebApiWsLiveClass();
void init(AsyncWebServer& server, Scheduler& scheduler); void init(AsyncWebServer& server, Scheduler& scheduler);
void reload();
private: private:
static void generateInverterCommonJsonResponse(JsonObject& root, std::shared_ptr<InverterAbstract> inv); static void generateInverterCommonJsonResponse(JsonObject& root, std::shared_ptr<InverterAbstract> inv);
@ -24,6 +25,7 @@ private:
void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len); void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len);
AsyncWebSocket _ws; AsyncWebSocket _ws;
AuthenticationMiddleware _simpleDigestAuth;
uint32_t _lastPublishStats[INV_MAX_COUNT] = { 0 }; uint32_t _lastPublishStats[INV_MAX_COUNT] = { 0 };

View File

@ -39,6 +39,12 @@ void WebApiClass::init(Scheduler& scheduler)
_server.begin(); _server.begin();
} }
void WebApiClass::reload()
{
_webApiWsConsole.reload();
_webApiWsLive.reload();
}
bool WebApiClass::checkCredentials(AsyncWebServerRequest* request) bool WebApiClass::checkCredentials(AsyncWebServerRequest* request)
{ {
CONFIG_T& config = Configuration.get(); CONFIG_T& config = Configuration.get();

View File

@ -71,6 +71,8 @@ void WebApiSecurityClass::onSecurityPost(AsyncWebServerRequest* request)
WebApi.writeConfig(retMsg); WebApi.writeConfig(retMsg);
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
WebApi.reload();
} }
void WebApiSecurityClass::onAuthenticateGet(AsyncWebServerRequest* request) void WebApiSecurityClass::onAuthenticateGet(AsyncWebServerRequest* request)

View File

@ -21,16 +21,27 @@ void WebApiWsConsoleClass::init(AsyncWebServer& server, Scheduler& scheduler)
scheduler.addTask(_wsCleanupTask); scheduler.addTask(_wsCleanupTask);
_wsCleanupTask.enable(); _wsCleanupTask.enable();
_simpleDigestAuth.setUsername(AUTH_USERNAME);
_simpleDigestAuth.setRealm("console websocket");
reload();
}
void WebApiWsConsoleClass::reload()
{
_ws.removeMiddleware(&_simpleDigestAuth);
auto const& config = Configuration.get();
if (config.Security.AllowReadonly) { return; }
_simpleDigestAuth.setPassword(config.Security.Password);
_ws.addMiddleware(&_simpleDigestAuth);
} }
void WebApiWsConsoleClass::wsCleanupTaskCb() void WebApiWsConsoleClass::wsCleanupTaskCb()
{ {
// see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients // see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients
_ws.cleanupClients(); _ws.cleanupClients();
if (Configuration.get().Security.AllowReadonly) {
_ws.setAuthentication("", "");
} else {
_ws.setAuthentication(AUTH_USERNAME, Configuration.get().Security.Password);
}
} }

View File

@ -36,18 +36,28 @@ void WebApiWsLiveClass::init(AsyncWebServer& server, Scheduler& scheduler)
scheduler.addTask(_sendDataTask); scheduler.addTask(_sendDataTask);
_sendDataTask.enable(); _sendDataTask.enable();
_simpleDigestAuth.setUsername(AUTH_USERNAME);
_simpleDigestAuth.setRealm("live websocket");
reload();
}
void WebApiWsLiveClass::reload()
{
_ws.removeMiddleware(&_simpleDigestAuth);
auto const& config = Configuration.get();
if (config.Security.AllowReadonly) { return; }
_simpleDigestAuth.setPassword(config.Security.Password);
_ws.addMiddleware(&_simpleDigestAuth);
} }
void WebApiWsLiveClass::wsCleanupTaskCb() void WebApiWsLiveClass::wsCleanupTaskCb()
{ {
// see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients // see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients
_ws.cleanupClients(); _ws.cleanupClients();
if (Configuration.get().Security.AllowReadonly) {
_ws.setAuthentication("", "");
} else {
_ws.setAuthentication(AUTH_USERNAME, Configuration.get().Security.Password);
}
} }
void WebApiWsLiveClass::sendDataTaskCb() void WebApiWsLiveClass::sendDataTaskCb()