Skip to content

Commit 3f13984

Browse files
authored
Bugfix: switching brokers with self signed certs. (#1007)
* Fixes BLE scanning being disabled after switching server fails. * Fixes crash caused by BLE scan results arriving while switching servers. * Adds documentation to use the server switching command.
1 parent 05dddf4 commit 3f13984

File tree

4 files changed

+105
-77
lines changed

4 files changed

+105
-77
lines changed

docs/use/gateway.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,27 @@ Server, port, and secure_flag are only required if changing connection to anothe
5757
If the new connection fails the gateway will fallback to the previous connection.
5858
:::
5959

60+
## Switching brokers and using self signed and client certificates
61+
62+
In the `user_config.h` file it is possible to specify multiple MQTT brokers and client certificates. These are commonly self signed and are supported by defining `MQTT_SECURE_SELF_SIGNED` as true or 1.
63+
Additonally, support for multiple brokers and client certificates has been added. To use this, it is required that the server certificate, client certificate, and client key are provided as their own constatnt string value as demonstrated in the file.
64+
To add more than one broker and switch between them it is necessary to provide all of the relevant certificates/keys and add their respective variable names in the `certs_array` structure, as shown in `user_config.h`, and changing the array size to the number of different connections -1.
65+
66+
To switch between these servers with an MQTT command message, the format is as follows:
67+
```
68+
mosquitto_pub -t "home/OpenMQTTGateway/commands/MQTTtoSYS/config" -m
69+
'{
70+
"mqtt_user": "user",
71+
"mqtt_pass": "password",
72+
"mqtt_server": "host",
73+
"mqtt_port": "port",
74+
"mqtt_secure": "true",
75+
"mqtt_cert_index":0
76+
}'
77+
```
78+
::: tip
79+
The `mqtt_cert_index` value corresponds to the 0 to X index of the `certs_array` in `user_config.h`.
80+
:::
6081

6182
# Firmware update from MQTT (ESP only)
6283

main/User_config.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
198198
# endif
199199

200200
# ifndef MQTT_SECURE_SELF_SIGNED_CLIENT
201-
# define MQTT_SECURE_SELF_SIGNED_CLIENT 1
201+
# define MQTT_SECURE_SELF_SIGNED_CLIENT 1 // If using a self signed certificate for the broker and not using client certificates set this to false or 0
202202
# endif
203203

204204
# ifndef MQTT_SECURE_SELF_SIGNED_INDEX_DEFAULT
@@ -219,9 +219,9 @@ const char* ss_client_cert PROGMEM = R"EOF("
219219
")EOF";
220220

221221
const char* ss_client_key PROGMEM = R"EOF("
222-
-----BEGIN CERTIFICATE-----
222+
-----BEGIN RSA PRIVATE KEY-----
223223
...
224-
-----END CERTIFICATE-----
224+
-----END RSA PRIVATE KEY-----
225225
")EOF";
226226

227227
struct ss_certs {

main/ZgatewayBT.ino

Lines changed: 67 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -623,85 +623,87 @@ class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
623623
}
624624

625625
void onResult(BLEAdvertisedDevice* advertisedDevice) {
626-
Log.trace(F("Creating BLE buffer" CR));
627-
JsonObject& BLEdata = getBTJsonObject();
628-
String mac_adress = advertisedDevice->getAddress().toString().c_str();
629-
mac_adress.toUpperCase();
630-
BLEdata.set("id", (char*)mac_adress.c_str());
631-
Log.notice(F("Device detected: %s" CR), (char*)mac_adress.c_str());
632-
BLEdevice* device = getDeviceByMac(BLEdata["id"].as<const char*>());
626+
if (!ProcessLock) {
627+
Log.trace(F("Creating BLE buffer" CR));
628+
JsonObject& BLEdata = getBTJsonObject();
629+
String mac_adress = advertisedDevice->getAddress().toString().c_str();
630+
mac_adress.toUpperCase();
631+
BLEdata.set("id", (char*)mac_adress.c_str());
632+
Log.notice(F("Device detected: %s" CR), (char*)mac_adress.c_str());
633+
BLEdevice* device = getDeviceByMac(BLEdata["id"].as<const char*>());
633634

634635
# if BLE_FILTER_CONNECTABLE
635-
if (device->connect) {
636-
Log.notice(F("Filtered connectable device" CR));
637-
return;
638-
}
636+
if (device->connect) {
637+
Log.notice(F("Filtered connectable device" CR));
638+
return;
639+
}
639640
# endif
640641

641-
if ((!oneWhite || isWhite(device)) && !isBlack(device)) { //if not black listed mac we go AND if we have no white mac or this mac is white we go out
642-
if (advertisedDevice->haveName())
643-
BLEdata.set("name", (char*)advertisedDevice->getName().c_str());
644-
if (advertisedDevice->haveManufacturerData()) {
645-
char* manufacturerdata = BLEUtils::buildHexData(NULL, (uint8_t*)advertisedDevice->getManufacturerData().data(), advertisedDevice->getManufacturerData().length());
646-
Log.trace(F("Manufacturer Data: %s" CR), manufacturerdata);
647-
BLEdata.set("manufacturerdata", manufacturerdata);
648-
free(manufacturerdata);
649-
}
650-
if (advertisedDevice->haveRSSI())
651-
BLEdata.set("rssi", (int)advertisedDevice->getRSSI());
652-
if (advertisedDevice->haveTXPower())
653-
BLEdata.set("txpower", (int8_t)advertisedDevice->getTXPower());
654-
if (advertisedDevice->haveRSSI() && !publishOnlySensors && hassPresence) {
655-
hass_presence(BLEdata); // this device has an rssi and we don't want only sensors so in consequence we can use it for home assistant room presence component
656-
}
657-
if (advertisedDevice->haveServiceData()) {
658-
int serviceDataCount = advertisedDevice->getServiceDataCount();
659-
Log.trace(F("Get services data number: %d" CR), serviceDataCount);
660-
for (int j = 0; j < serviceDataCount; j++) {
661-
std::string service_data = convertServiceData(advertisedDevice->getServiceData(j));
662-
Log.trace(F("Service data: %s" CR), service_data.c_str());
663-
BLEdata.set("servicedata", (char*)service_data.c_str());
664-
std::string serviceDatauuid = advertisedDevice->getServiceDataUUID(j).toString();
665-
Log.trace(F("Service data UUID: %s" CR), (char*)serviceDatauuid.c_str());
666-
BLEdata.set("servicedatauuid", (char*)serviceDatauuid.c_str());
667-
process_bledata(BLEdata); // this will force to resolve all the service data
642+
if ((!oneWhite || isWhite(device)) && !isBlack(device)) { //if not black listed mac we go AND if we have no white mac or this mac is white we go out
643+
if (advertisedDevice->haveName())
644+
BLEdata.set("name", (char*)advertisedDevice->getName().c_str());
645+
if (advertisedDevice->haveManufacturerData()) {
646+
char* manufacturerdata = BLEUtils::buildHexData(NULL, (uint8_t*)advertisedDevice->getManufacturerData().data(), advertisedDevice->getManufacturerData().length());
647+
Log.trace(F("Manufacturer Data: %s" CR), manufacturerdata);
648+
BLEdata.set("manufacturerdata", manufacturerdata);
649+
free(manufacturerdata);
668650
}
669-
670-
if (serviceDataCount > 1) {
671-
BLEdata.remove("servicedata");
672-
BLEdata.remove("servicedatauuid");
673-
674-
int msglen = BLEdata.measureLength() + 1;
675-
char jsonmsg[msglen];
676-
char jsonmsgb[msglen];
677-
BLEdata.printTo(jsonmsgb, sizeof(jsonmsgb));
651+
if (advertisedDevice->haveRSSI())
652+
BLEdata.set("rssi", (int)advertisedDevice->getRSSI());
653+
if (advertisedDevice->haveTXPower())
654+
BLEdata.set("txpower", (int8_t)advertisedDevice->getTXPower());
655+
if (advertisedDevice->haveRSSI() && !publishOnlySensors && hassPresence) {
656+
hass_presence(BLEdata); // this device has an rssi and we don't want only sensors so in consequence we can use it for home assistant room presence component
657+
}
658+
if (advertisedDevice->haveServiceData()) {
659+
int serviceDataCount = advertisedDevice->getServiceDataCount();
660+
Log.trace(F("Get services data number: %d" CR), serviceDataCount);
678661
for (int j = 0; j < serviceDataCount; j++) {
679-
strcpy(jsonmsg, jsonmsgb); // the parse _destroys_ the message buffer
680-
JsonObject& BLEdataLocal = getBTJsonObject(jsonmsg, j == 0); // note, that first time we will get here the BLEdata itself; haPresence for the first msg
681-
if (!BLEdataLocal.containsKey("id")) { // would crash without id
682-
Log.trace("Json parsing error for %s" CR, jsonmsgb);
683-
break;
684-
}
685662
std::string service_data = convertServiceData(advertisedDevice->getServiceData(j));
663+
Log.trace(F("Service data: %s" CR), service_data.c_str());
664+
BLEdata.set("servicedata", (char*)service_data.c_str());
686665
std::string serviceDatauuid = advertisedDevice->getServiceDataUUID(j).toString();
666+
Log.trace(F("Service data UUID: %s" CR), (char*)serviceDatauuid.c_str());
667+
BLEdata.set("servicedatauuid", (char*)serviceDatauuid.c_str());
668+
process_bledata(BLEdata); // this will force to resolve all the service data
669+
}
687670

688-
int last = atomic_load_explicit(&jsonBTBufferQueueLast, ::memory_order_seq_cst) % BTQueueSize;
689-
int size1 = jsonBTBufferQueue[last].buffer.size();
690-
BLEdataLocal.set("servicedata", (char*)service_data.c_str());
691-
int size2 = jsonBTBufferQueue[last].buffer.size();
692-
BLEdataLocal.set("servicedatauuid", (char*)serviceDatauuid.c_str());
693-
int size3 = jsonBTBufferQueue[last].buffer.size();
694-
Log.trace("Buffersize for %d : %d -> %d -> %d" CR, j, size1, size2, size3);
695-
PublishDeviceData(BLEdataLocal);
671+
if (serviceDataCount > 1) {
672+
BLEdata.remove("servicedata");
673+
BLEdata.remove("servicedatauuid");
674+
675+
int msglen = BLEdata.measureLength() + 1;
676+
char jsonmsg[msglen];
677+
char jsonmsgb[msglen];
678+
BLEdata.printTo(jsonmsgb, sizeof(jsonmsgb));
679+
for (int j = 0; j < serviceDataCount; j++) {
680+
strcpy(jsonmsg, jsonmsgb); // the parse _destroys_ the message buffer
681+
JsonObject& BLEdataLocal = getBTJsonObject(jsonmsg, j == 0); // note, that first time we will get here the BLEdata itself; haPresence for the first msg
682+
if (!BLEdataLocal.containsKey("id")) { // would crash without id
683+
Log.trace("Json parsing error for %s" CR, jsonmsgb);
684+
break;
685+
}
686+
std::string service_data = convertServiceData(advertisedDevice->getServiceData(j));
687+
std::string serviceDatauuid = advertisedDevice->getServiceDataUUID(j).toString();
688+
689+
int last = atomic_load_explicit(&jsonBTBufferQueueLast, ::memory_order_seq_cst) % BTQueueSize;
690+
int size1 = jsonBTBufferQueue[last].buffer.size();
691+
BLEdataLocal.set("servicedata", (char*)service_data.c_str());
692+
int size2 = jsonBTBufferQueue[last].buffer.size();
693+
BLEdataLocal.set("servicedatauuid", (char*)serviceDatauuid.c_str());
694+
int size3 = jsonBTBufferQueue[last].buffer.size();
695+
Log.trace("Buffersize for %d : %d -> %d -> %d" CR, j, size1, size2, size3);
696+
PublishDeviceData(BLEdataLocal);
697+
}
698+
} else {
699+
PublishDeviceData(BLEdata, false); // easy case
696700
}
697701
} else {
698-
PublishDeviceData(BLEdata, false); // easy case
702+
PublishDeviceData(BLEdata); // PublishDeviceData has its own logic whether it needs to publish the json or not
699703
}
700704
} else {
701-
PublishDeviceData(BLEdata); // PublishDeviceData has its own logic whether it needs to publish the json or not
705+
Log.trace(F("Filtered mac device" CR));
702706
}
703-
} else {
704-
Log.trace(F("Filtered mac device" CR));
705707
}
706708
}
707709
};

main/main.ino

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,7 @@ void setupTLS(bool self_signed, uint8_t index) {
868868
WiFiClientSecure* sClient = (WiFiClientSecure*)eClient;
869869
# if MQTT_SECURE_SELF_SIGNED
870870
if (self_signed) {
871+
Log.notice(F("Using self signed cert index %u" CR), index);
871872
# if defined(ESP32)
872873
sClient->setCACert(certs_array[index].server_cert);
873874
# if MQTT_SECURE_SELF_SIGNED_CLIENT
@@ -1881,33 +1882,32 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding
18811882
}
18821883

18831884
if (SYSdata.containsKey("mqtt_user") && SYSdata.containsKey("mqtt_pass")) {
1884-
# if defined(ZgatewayBT) && defined(ESP32)
1885-
stopProcessing();
1886-
# endif
1887-
client.disconnect();
18881885
bool update_server = false;
18891886
bool secure_connect = SYSdata.get<bool>("mqtt_secure");
18901887
void* prev_client = nullptr;
1891-
bool use_ss_cert = SYSdata.containsKey("mqtt_ss_cert");
1888+
bool use_ss_cert = SYSdata.containsKey("mqtt_cert_index");
18921889
uint8_t cert_index = mqtt_ss_index;
18931890

18941891
if (SYSdata.containsKey("mqtt_server") && SYSdata.containsKey("mqtt_port")) {
18951892
if (!SYSdata.containsKey("mqtt_secure")) {
1896-
Log.warning(F("mqtt_server provided without mqtt_secure defined - ignoring command" CR));
1893+
Log.error(F("mqtt_server provided without mqtt_secure defined - ignoring command" CR));
18971894
return;
18981895
}
18991896
# if MQTT_SECURE_SELF_SIGNED
19001897
if (use_ss_cert) {
1901-
cert_index = SYSdata.get<uint8_t>("mqtt_ss_cert");
1898+
cert_index = SYSdata.get<uint8_t>("mqtt_cert_index");
19021899
if (cert_index >= sizeof(certs_array) / sizeof(ss_certs)) {
1903-
Log.warning(F("mqtt_ss_cert index invalid - ignoring command" CR));
1900+
Log.error(F("mqtt_cert_index invalid - ignoring command" CR));
19041901
return;
19051902
}
19061903
}
19071904
# endif
19081905

1906+
# if defined(ZgatewayBT) && defined(ESP32)
1907+
stopProcessing();
1908+
# endif
1909+
client.disconnect();
19091910
update_server = true;
1910-
19111911
if (secure_connect != mqtt_secure) {
19121912
prev_client = eClient;
19131913
if (!mqtt_secure) {
@@ -1925,6 +1925,11 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding
19251925
}
19261926

19271927
client.setServer(SYSdata.get<const char*>("mqtt_server"), SYSdata.get<unsigned int>("mqtt_port"));
1928+
} else {
1929+
# if defined(ZgatewayBT) && defined(ESP32)
1930+
stopProcessing();
1931+
# endif
1932+
client.disconnect();
19281933
}
19291934

19301935
String prev_user = mqtt_user;

0 commit comments

Comments
 (0)