Merge branch 'pr801' into dev

This commit is contained in:
Thomas Basler 2023-04-12 19:35:58 +02:00
commit ebaccc9b3f
13 changed files with 103 additions and 28 deletions

View File

@ -19,7 +19,7 @@
#define MQTT_MAX_PASSWORD_STRLEN 64
#define MQTT_MAX_TOPIC_STRLEN 32
#define MQTT_MAX_LWTVALUE_STRLEN 20
#define MQTT_MAX_ROOT_CA_CERT_STRLEN 2560
#define MQTT_MAX_CERT_STRLEN 2560
#define INV_MAX_NAME_STRLEN 31
#define INV_MAX_COUNT 10
@ -89,7 +89,10 @@ struct CONFIG_T {
char Mqtt_Hass_Topic[MQTT_MAX_TOPIC_STRLEN + 1];
bool Mqtt_Hass_IndividualPanels;
bool Mqtt_Tls;
char Mqtt_RootCaCert[MQTT_MAX_ROOT_CA_CERT_STRLEN + 1];
char Mqtt_RootCaCert[MQTT_MAX_CERT_STRLEN + 1];
bool Mqtt_TlsCertLogin;
char Mqtt_ClientCert[MQTT_MAX_CERT_STRLEN + 1];
char Mqtt_ClientKey[MQTT_MAX_CERT_STRLEN + 1];
char Mqtt_Hostname[MQTT_MAX_HOSTNAME_STRLEN + 1];

View File

@ -3,7 +3,7 @@
#include <ESPAsyncWebServer.h>
#define MQTT_JSON_DOC_SIZE 3072
#define MQTT_JSON_DOC_SIZE 10240
class WebApiMqttClass {
public:
@ -14,7 +14,7 @@ private:
void onMqttStatus(AsyncWebServerRequest* request);
void onMqttAdminGet(AsyncWebServerRequest* request);
void onMqttAdminPost(AsyncWebServerRequest* request);
String getRootCaCertInfo(const char* cert);
String getTlsCertInfo(const char* cert);
AsyncWebServer* _server;
};

View File

@ -66,6 +66,9 @@
"mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n" \
"emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n" \
"-----END CERTIFICATE-----\n"
#define MQTT_TLSCERTLOGIN false
#define MQTT_TLSCLIENTCERT ""
#define MQTT_TLSCLIENTKEY ""
#define MQTT_LWT_TOPIC "dtu/status"
#define MQTT_LWT_ONLINE "online"
#define MQTT_LWT_OFFLINE "offline"

View File

@ -65,6 +65,9 @@ bool ConfigurationClass::write()
JsonObject mqtt_tls = mqtt.createNestedObject("tls");
mqtt_tls["enabled"] = config.Mqtt_Tls;
mqtt_tls["root_ca_cert"] = config.Mqtt_RootCaCert;
mqtt_tls["certlogin"] = config.Mqtt_TlsCertLogin;
mqtt_tls["client_cert"] = config.Mqtt_ClientCert;
mqtt_tls["client_key"] = config.Mqtt_ClientKey;
JsonObject mqtt_hass = mqtt.createNestedObject("hass");
mqtt_hass["enabled"] = config.Mqtt_Hass_Enabled;
@ -202,6 +205,9 @@ bool ConfigurationClass::read()
JsonObject mqtt_tls = mqtt["tls"];
config.Mqtt_Tls = mqtt_tls["enabled"] | MQTT_TLS;
strlcpy(config.Mqtt_RootCaCert, mqtt_tls["root_ca_cert"] | MQTT_ROOT_CA_CERT, sizeof(config.Mqtt_RootCaCert));
config.Mqtt_TlsCertLogin = mqtt_tls["certlogin"] | MQTT_TLSCERTLOGIN;
strlcpy(config.Mqtt_ClientCert, mqtt_tls["client_cert"] | MQTT_TLSCLIENTCERT, sizeof(config.Mqtt_ClientCert));
strlcpy(config.Mqtt_ClientKey, mqtt_tls["client_key"] | MQTT_TLSCLIENTKEY, sizeof(config.Mqtt_ClientKey));
JsonObject mqtt_hass = mqtt["hass"];
config.Mqtt_Hass_Enabled = mqtt_hass["enabled"] | MQTT_HASS_ENABLED;

View File

@ -104,7 +104,12 @@ void MqttSettingsClass::performConnect()
if (config.Mqtt_Tls) {
static_cast<espMqttClientSecure*>(mqttClient)->setCACert(config.Mqtt_RootCaCert);
static_cast<espMqttClientSecure*>(mqttClient)->setServer(config.Mqtt_Hostname, config.Mqtt_Port);
static_cast<espMqttClientSecure*>(mqttClient)->setCredentials(config.Mqtt_Username, config.Mqtt_Password);
if (config.Mqtt_TlsCertLogin) {
static_cast<espMqttClientSecure*>(mqttClient)->setCertificate(config.Mqtt_ClientCert);
static_cast<espMqttClientSecure*>(mqttClient)->setPrivateKey(config.Mqtt_ClientKey);
} else {
static_cast<espMqttClientSecure*>(mqttClient)->setCredentials(config.Mqtt_Username, config.Mqtt_Password);
}
static_cast<espMqttClientSecure*>(mqttClient)->setWill(willTopic.c_str(), 2, config.Mqtt_Retain, config.Mqtt_LwtValue_Offline);
static_cast<espMqttClientSecure*>(mqttClient)->setClientId(clientId.c_str());
static_cast<espMqttClientSecure*>(mqttClient)->onConnect(std::bind(&MqttSettingsClass::onMqttConnect, this, _1));

View File

@ -44,7 +44,9 @@ void WebApiMqttClass::onMqttStatus(AsyncWebServerRequest* request)
root["mqtt_connected"] = MqttSettings.getConnected();
root["mqtt_retain"] = config.Mqtt_Retain;
root["mqtt_tls"] = config.Mqtt_Tls;
root["mqtt_root_ca_cert_info"] = getRootCaCertInfo(config.Mqtt_RootCaCert);
root["mqtt_root_ca_cert_info"] = getTlsCertInfo(config.Mqtt_RootCaCert);
root["mqtt_tls_cert_login"] = config.Mqtt_TlsCertLogin;
root["mqtt_client_cert_info"] = getTlsCertInfo(config.Mqtt_ClientCert);
root["mqtt_lwt_topic"] = String(config.Mqtt_Topic) + config.Mqtt_LwtTopic;
root["mqtt_publish_interval"] = config.Mqtt_PublishInterval;
root["mqtt_hass_enabled"] = config.Mqtt_Hass_Enabled;
@ -76,6 +78,9 @@ void WebApiMqttClass::onMqttAdminGet(AsyncWebServerRequest* request)
root["mqtt_retain"] = config.Mqtt_Retain;
root["mqtt_tls"] = config.Mqtt_Tls;
root["mqtt_root_ca_cert"] = config.Mqtt_RootCaCert;
root["mqtt_tls_cert_login"] = config.Mqtt_TlsCertLogin;
root["mqtt_client_cert"] = config.Mqtt_ClientCert;
root["mqtt_client_key"] = config.Mqtt_ClientKey;
root["mqtt_lwt_topic"] = config.Mqtt_LwtTopic;
root["mqtt_lwt_online"] = config.Mqtt_LwtValue_Online;
root["mqtt_lwt_offline"] = config.Mqtt_LwtValue_Offline;
@ -137,6 +142,9 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
&& root.containsKey("mqtt_topic")
&& root.containsKey("mqtt_retain")
&& root.containsKey("mqtt_tls")
&& root.containsKey("mqtt_tls_cert_login")
&& root.containsKey("mqtt_client_cert")
&& root.containsKey("mqtt_client_key")
&& root.containsKey("mqtt_lwt_topic")
&& root.containsKey("mqtt_lwt_online")
&& root.containsKey("mqtt_lwt_offline")
@ -164,7 +172,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
}
if (root["mqtt_username"].as<String>().length() > MQTT_MAX_USERNAME_STRLEN) {
retMsg["message"] = "Username must not longer then " STR(MQTT_MAX_USERNAME_STRLEN) " characters!";
retMsg["message"] = "Username must not be longer than " STR(MQTT_MAX_USERNAME_STRLEN) " characters!";
retMsg["code"] = WebApiError::MqttUsernameLength;
retMsg["param"]["max"] = MQTT_MAX_USERNAME_STRLEN;
response->setLength();
@ -172,7 +180,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
return;
}
if (root["mqtt_password"].as<String>().length() > MQTT_MAX_PASSWORD_STRLEN) {
retMsg["message"] = "Password must not longer then " STR(MQTT_MAX_PASSWORD_STRLEN) " characters!";
retMsg["message"] = "Password must not be longer than " STR(MQTT_MAX_PASSWORD_STRLEN) " characters!";
retMsg["code"] = WebApiError::MqttPasswordLength;
retMsg["param"]["max"] = MQTT_MAX_PASSWORD_STRLEN;
response->setLength();
@ -180,7 +188,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
return;
}
if (root["mqtt_topic"].as<String>().length() > MQTT_MAX_TOPIC_STRLEN) {
retMsg["message"] = "Topic must not longer then " STR(MQTT_MAX_TOPIC_STRLEN) " characters!";
retMsg["message"] = "Topic must not be longer than " STR(MQTT_MAX_TOPIC_STRLEN) " characters!";
retMsg["code"] = WebApiError::MqttTopicLength;
retMsg["param"]["max"] = MQTT_MAX_TOPIC_STRLEN;
response->setLength();
@ -197,7 +205,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
}
if (!root["mqtt_topic"].as<String>().endsWith("/")) {
retMsg["message"] = "Topic must end with slash (/)!";
retMsg["message"] = "Topic must end with a slash (/)!";
retMsg["code"] = WebApiError::MqttTopicTrailingSlash;
response->setLength();
request->send(response);
@ -212,17 +220,19 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
return;
}
if (root["mqtt_root_ca_cert"].as<String>().length() > MQTT_MAX_ROOT_CA_CERT_STRLEN) {
retMsg["message"] = "Certificate must not longer then " STR(MQTT_MAX_ROOT_CA_CERT_STRLEN) " characters!";
if (root["mqtt_root_ca_cert"].as<String>().length() > MQTT_MAX_CERT_STRLEN
|| root["mqtt_client_cert"].as<String>().length() > MQTT_MAX_CERT_STRLEN
|| root["mqtt_client_key"].as<String>().length() > MQTT_MAX_CERT_STRLEN) {
retMsg["message"] = "Certificates must not be longer than " STR(MQTT_MAX_CERT_STRLEN) " characters!";
retMsg["code"] = WebApiError::MqttCertificateLength;
retMsg["param"]["max"] = MQTT_MAX_ROOT_CA_CERT_STRLEN;
retMsg["param"]["max"] = MQTT_MAX_CERT_STRLEN;
response->setLength();
request->send(response);
return;
}
if (root["mqtt_lwt_topic"].as<String>().length() > MQTT_MAX_TOPIC_STRLEN) {
retMsg["message"] = "LWT topic must not longer then " STR(MQTT_MAX_TOPIC_STRLEN) " characters!";
retMsg["message"] = "LWT topic must not be longer than " STR(MQTT_MAX_TOPIC_STRLEN) " characters!";
retMsg["code"] = WebApiError::MqttLwtTopicLength;
retMsg["param"]["max"] = MQTT_MAX_TOPIC_STRLEN;
response->setLength();
@ -239,7 +249,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
}
if (root["mqtt_lwt_online"].as<String>().length() > MQTT_MAX_LWTVALUE_STRLEN) {
retMsg["message"] = "LWT online value must not longer then " STR(MQTT_MAX_LWTVALUE_STRLEN) " characters!";
retMsg["message"] = "LWT online value must not be longer than " STR(MQTT_MAX_LWTVALUE_STRLEN) " characters!";
retMsg["code"] = WebApiError::MqttLwtOnlineLength;
retMsg["param"]["max"] = MQTT_MAX_LWTVALUE_STRLEN;
response->setLength();
@ -248,7 +258,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
}
if (root["mqtt_lwt_offline"].as<String>().length() > MQTT_MAX_LWTVALUE_STRLEN) {
retMsg["message"] = "LWT offline value must not longer then " STR(MQTT_MAX_LWTVALUE_STRLEN) " characters!";
retMsg["message"] = "LWT offline value must not be longer than " STR(MQTT_MAX_LWTVALUE_STRLEN) " characters!";
retMsg["code"] = WebApiError::MqttLwtOfflineLength;
retMsg["param"]["max"] = MQTT_MAX_LWTVALUE_STRLEN;
response->setLength();
@ -268,7 +278,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
if (root["mqtt_hass_enabled"].as<bool>()) {
if (root["mqtt_hass_topic"].as<String>().length() > MQTT_MAX_TOPIC_STRLEN) {
retMsg["message"] = "Hass topic must not longer then " STR(MQTT_MAX_TOPIC_STRLEN) " characters!";
retMsg["message"] = "Hass topic must not be longer than " STR(MQTT_MAX_TOPIC_STRLEN) " characters!";
retMsg["code"] = WebApiError::MqttHassTopicLength;
retMsg["param"]["max"] = MQTT_MAX_TOPIC_STRLEN;
response->setLength();
@ -291,6 +301,9 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
config.Mqtt_Retain = root["mqtt_retain"].as<bool>();
config.Mqtt_Tls = root["mqtt_tls"].as<bool>();
strlcpy(config.Mqtt_RootCaCert, root["mqtt_root_ca_cert"].as<String>().c_str(), sizeof(config.Mqtt_RootCaCert));
config.Mqtt_TlsCertLogin = root["mqtt_tls_cert_login"].as<bool>();
strlcpy(config.Mqtt_ClientCert, root["mqtt_client_cert"].as<String>().c_str(), sizeof(config.Mqtt_ClientCert));
strlcpy(config.Mqtt_ClientKey, root["mqtt_client_key"].as<String>().c_str(), sizeof(config.Mqtt_ClientKey));
config.Mqtt_Port = root["mqtt_port"].as<uint>();
strlcpy(config.Mqtt_Hostname, root["mqtt_hostname"].as<String>().c_str(), sizeof(config.Mqtt_Hostname));
strlcpy(config.Mqtt_Username, root["mqtt_username"].as<String>().c_str(), sizeof(config.Mqtt_Username));
@ -318,23 +331,23 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
MqttHandleHass.forceUpdate();
}
String WebApiMqttClass::getRootCaCertInfo(const char* cert)
String WebApiMqttClass::getTlsCertInfo(const char* cert)
{
char rootCaCertInfo[1024] = "";
char tlsCertInfo[1024] = "";
mbedtls_x509_crt global_cacert;
mbedtls_x509_crt tlsCert;
strlcpy(rootCaCertInfo, "Can't parse root ca", sizeof(rootCaCertInfo));
strlcpy(tlsCertInfo, "Can't parse TLS certificate", sizeof(tlsCertInfo));
mbedtls_x509_crt_init(&global_cacert);
int ret = mbedtls_x509_crt_parse(&global_cacert, const_cast<unsigned char*>((unsigned char*)cert), 1 + strlen(cert));
mbedtls_x509_crt_init(&tlsCert);
int ret = mbedtls_x509_crt_parse(&tlsCert, const_cast<unsigned char*>((unsigned char*)cert), 1 + strlen(cert));
if (ret < 0) {
snprintf(rootCaCertInfo, sizeof(rootCaCertInfo), "Can't parse root ca: mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
mbedtls_x509_crt_free(&global_cacert);
snprintf(tlsCertInfo, sizeof(tlsCertInfo), "Can't parse TLS certificate: mbedtls_x509_crt_parse returned -0x%x\n\n", -ret);
mbedtls_x509_crt_free(&tlsCert);
return "";
}
mbedtls_x509_crt_info(rootCaCertInfo, sizeof(rootCaCertInfo) - 1, "", &global_cacert);
mbedtls_x509_crt_free(&global_cacert);
mbedtls_x509_crt_info(tlsCertInfo, sizeof(tlsCertInfo) - 1, "", &tlsCert);
mbedtls_x509_crt_free(&tlsCert);
return rootCaCertInfo;
return tlsCertInfo;
}

View File

@ -259,6 +259,8 @@
"Retain": "Retain",
"Tls": "TLS",
"RootCertifcateInfo": "Root CA-Zertifikat-Informationen",
"TlsCertLogin": "Anmeldung mit TLS Zertifikat",
"ClientCertifcateInfo": "Client Zertifikat-Informationen",
"HassSummary": "Home Assistant MQTT-Auto-Discovery Konfigurationszusammenfassung",
"Expire": "Ablaufen",
"IndividualPanels": "Einzelne Paneele",
@ -387,6 +389,9 @@
"EnableRetain": "Retain Flag aktivieren",
"EnableTls": "TLS aktivieren",
"RootCa": "CA-Root-Zertifikat (Standard Letsencrypt):",
"TlsCertLoginEnable": "TLS Zertifikat Login",
"ClientCert": "TLS Client-Zertifikat:",
"ClientKey": "TLS Client-Key:",
"LwtParameters": "LWT-Parameter",
"LwtTopic": "LWT-Topic:",
"LwtTopicHint": "LWT-Topic, wird der Basis Topic angehängt",

View File

@ -259,6 +259,8 @@
"Retain": "Retain",
"Tls": "TLS",
"RootCertifcateInfo": "Root CA Certifcate Info",
"TlsCertLogin": "Login with TLS Certificate",
"ClientCertifcateInfo": "Client Certifcate Info",
"HassSummary": "Home Assistant MQTT Auto Discovery Configuration Summary",
"Expire": "Expire",
"IndividualPanels": "Individual Panels",
@ -387,6 +389,9 @@
"EnableRetain": "Enable Retain Flag",
"EnableTls": "Enable TLS",
"RootCa": "CA-Root-Certificate (default Letsencrypt):",
"TlsCertLoginEnable": "Enable TLS Certificate Login",
"ClientCert": "TLS Client-Certificate:",
"ClientKey": "TLS Client-Key:",
"LwtParameters": "LWT Parameters",
"LwtTopic": "LWT Topic:",
"LwtTopicHint": "LWT topic, will be append base topic",

View File

@ -259,6 +259,8 @@
"Retain": "Conserver",
"Tls": "TLS",
"RootCertifcateInfo": "Informations sur le certificat de l'autorité de certification racine",
"TlsCertLogin": "Connexion avec un certificat TLS",
"ClientCertifcateInfo": "Informations sur le certificat du client",
"HassSummary": "Résumé de la configuration de la découverte automatique du MQTT de Home Assistant",
"Expire": "Expiration",
"IndividualPanels": "Panneaux individuels",
@ -387,6 +389,9 @@
"EnableRetain": "Activation du maintien",
"EnableTls": "Activer le TLS",
"RootCa": "Certificat CA-Root (par défaut Letsencrypt)",
"TlsCertLoginEnable": "Activer la connexion par certificat TLS",
"ClientCert": "Certificat client TLS:",
"ClientKey": "Clé client TLS:",
"LwtParameters": "Paramètres LWT",
"LwtTopic": "Sujet LWT",
"LwtTopicHint": "Sujet LWT, sera ajouté comme sujet de base",

View File

@ -9,6 +9,9 @@ export interface MqttConfig {
mqtt_retain: boolean;
mqtt_tls: boolean;
mqtt_root_ca_cert: string;
mqtt_tls_cert_login: boolean;
mqtt_client_cert: string;
mqtt_client_key: string;
mqtt_lwt_topic: string;
mqtt_lwt_online: string;
mqtt_lwt_offline: string;

View File

@ -8,6 +8,8 @@ export interface MqttStatus {
mqtt_retain: boolean;
mqtt_tls: boolean;
mqtt_root_ca_cert_info: string;
mqtt_tls_cert_login: boolean;
mqtt_client_cert_info: string;
mqtt_connected: boolean;
mqtt_hass_enabled: boolean;
mqtt_hass_expire: boolean;

View File

@ -60,6 +60,21 @@
:label="$t('mqttadmin.RootCa')"
v-model="mqttConfigList.mqtt_root_ca_cert"
type="textarea" maxlength="2560" rows="10"/>
<InputElement v-show="mqttConfigList.mqtt_tls"
:label="$t('mqttadmin.TlsCertLoginEnable')"
v-model="mqttConfigList.mqtt_tls_cert_login"
type="checkbox"/>
<InputElement v-show="mqttConfigList.mqtt_tls_cert_login"
:label="$t('mqttadmin.ClientCert')"
v-model="mqttConfigList.mqtt_client_cert"
type="textarea" maxlength="2560" rows="10"/>
<InputElement v-show="mqttConfigList.mqtt_tls_cert_login"
:label="$t('mqttadmin.ClientKey')"
v-model="mqttConfigList.mqtt_client_key"
type="textarea" maxlength="2560" rows="10"/>
</CardElement>
<CardElement :text="$t('mqttadmin.LwtParameters')" textVariant="text-bg-primary" add-space

View File

@ -46,6 +46,16 @@
<th>{{ $t('mqttinfo.RootCertifcateInfo') }}</th>
<td>{{ mqttDataList.mqtt_root_ca_cert_info }}</td>
</tr>
<tr>
<th>{{ $t('mqttinfo.TlsCertLogin') }}</th>
<td>
<StatusBadge :status="mqttDataList.mqtt_tls_cert_login" true_text="mqttinfo.Enabled" false_text="mqttinfo.Disabled" />
</td>
</tr>
<tr v-show="mqttDataList.mqtt_tls_cert_login">
<th>{{ $t('mqttinfo.ClientCertifcateInfo') }}</th>
<td>{{ mqttDataList.mqtt_client_cert_info }}</td>
</tr>
</tbody>
</table>
</div>