Skip to content

Commit 1309b4e

Browse files
authored
Merge pull request #57 from OpenEVSE/secure_mqtt
Secure MQTT support
2 parents fd65fba + 6a3edb0 commit 1309b4e

File tree

7 files changed

+101
-30
lines changed

7 files changed

+101
-30
lines changed

platformio.ini

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ version = -DBUILD_TAG=3.0.3
3434
monitor_speed = 115200
3535
lib_deps =
3636
37-
37+
3838
extra_scripts = scripts/extra_script.py
3939
debug_flags =
4040
-DENABLE_DEBUG
@@ -56,10 +56,12 @@ build_flags =
5656
-DMG_SSL_MBED_DUMMY_RANDOM=1
5757
-DMG_SSL_IF=MG_SSL_IF_MBEDTLS
5858
-DMG_SSL_IF_MBEDTLS_FREE_CERTS=1
59-
-DMG_SSL_IF_MBEDTLS_MAX_FRAG_LEN=2048
59+
#-DMG_SSL_IF_MBEDTLS_MAX_FRAG_LEN=2048
6060
#-DARDUINO_MONGOOSE_DEFAULT_STREAM_BUFFER=2048
6161
#-DARDUINO_MONGOOSE_SEND_BUFFER_SIZE=2048
6262
#-DENABLE_DEBUG
63+
#-DCS_ENABLE_DEBUG
64+
#-DMBEDTLS_DEBUG_C
6365
-DMG_ENABLE_SNTP=1
6466
build_flags_esp8266 =
6567
-DMG_ESP8266
@@ -77,6 +79,7 @@ src_build_flags_esp32_gateway =
7779
-DRAPI_PORT=Serial2
7880
-DENABLE_WIRED_ETHERNET
7981
-DRANDOM_SEED_CHANNEL=1
82+
monitor_flags = --filter=esp8266_exception_decoder
8083

8184
# specify exact Arduino ESP SDK version, requires platformio 3.5+ (curently dev version)
8285
# http://docs.platformio.org/en/latest/projectconf/section_env_general.html#platform

src/app_config.cpp

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ String emoncms_fingerprint = "";
2929

3030
// MQTT Settings
3131
String mqtt_server = "";
32+
uint32_t mqtt_port = 1883;
3233
String mqtt_topic = "";
3334
String mqtt_user = "";
3435
String mqtt_pass = "";
@@ -47,7 +48,8 @@ uint32_t flags;
4748
#define EEPROM_EMON_API_KEY_SIZE 33
4849
#define EEPROM_EMON_SERVER_SIZE 45
4950
#define EEPROM_EMON_NODE_SIZE 32
50-
#define EEPROM_MQTT_SERVER_SIZE 45
51+
#define EEPROM_MQTT_SERVER_V1_SIZE 45
52+
#define EEPROM_MQTT_SERVER_SIZE 96
5153
#define EEPROM_MQTT_TOPIC_SIZE 32
5254
#define EEPROM_MQTT_USER_SIZE 32
5355
#define EEPROM_MQTT_PASS_SIZE 64
@@ -59,6 +61,7 @@ uint32_t flags;
5961
#define EEPROM_OHM_KEY_SIZE 10
6062
#define EEPROM_FLAGS_SIZE 4
6163
#define EEPROM_HOSTNAME_SIZE 32
64+
#define EEPROM_MQTT_PORT_SIZE 4
6265
#define EEPROM_SNTP_HOST_SIZE 45
6366
#define EEPROM_TIME_ZONE_SIZE 80
6467
#define EEPROM_SIZE 1024
@@ -71,9 +74,9 @@ uint32_t flags;
7174
#define EEPROM_EMON_SERVER_END (EEPROM_EMON_SERVER_START + EEPROM_EMON_SERVER_SIZE)
7275
#define EEPROM_EMON_NODE_START EEPROM_EMON_SERVER_END
7376
#define EEPROM_EMON_NODE_END (EEPROM_EMON_NODE_START + EEPROM_EMON_NODE_SIZE)
74-
#define EEPROM_MQTT_SERVER_START EEPROM_EMON_NODE_END
75-
#define EEPROM_MQTT_SERVER_END (EEPROM_MQTT_SERVER_START + EEPROM_MQTT_SERVER_SIZE)
76-
#define EEPROM_MQTT_TOPIC_START EEPROM_MQTT_SERVER_END
77+
#define EEPROM_MQTT_SERVER_V1_START EEPROM_EMON_NODE_END
78+
#define EEPROM_MQTT_SERVER_V1_END (EEPROM_MQTT_SERVER_V1_START + EEPROM_MQTT_SERVER_V1_SIZE)
79+
#define EEPROM_MQTT_TOPIC_START EEPROM_MQTT_SERVER_V1_END
7780
#define EEPROM_MQTT_TOPIC_END (EEPROM_MQTT_TOPIC_START + EEPROM_MQTT_TOPIC_SIZE)
7881
#define EEPROM_MQTT_USER_START EEPROM_MQTT_TOPIC_END
7982
#define EEPROM_MQTT_USER_END (EEPROM_MQTT_USER_START + EEPROM_MQTT_USER_SIZE)
@@ -101,7 +104,11 @@ uint32_t flags;
101104
#define EEPROM_SNTP_HOST_END (EEPROM_SNTP_HOST_START + EEPROM_SNTP_HOST_SIZE)
102105
#define EEPROM_TIME_ZONE_START EEPROM_SNTP_HOST_END
103106
#define EEPROM_TIME_ZONE_END (EEPROM_TIME_ZONE_START + EEPROM_TIME_ZONE_SIZE)
104-
#define EEPROM_CONFIG_END EEPROM_TIME_ZONE_END
107+
#define EEPROM_MQTT_SERVER_START EEPROM_TIME_ZONE_END
108+
#define EEPROM_MQTT_SERVER_END (EEPROM_MQTT_SERVER_START + EEPROM_MQTT_SERVER_SIZE)
109+
#define EEPROM_MQTT_PORT_START EEPROM_MQTT_SERVER_END
110+
#define EEPROM_MQTT_PORT_END (EEPROM_MQTT_PORT_START + EEPROM_MQTT_PORT_SIZE)
111+
#define EEPROM_CONFIG_END EEPROM_MQTT_PORT_END
105112

106113
#if EEPROM_CONFIG_END > EEPROM_SIZE
107114
#error EEPROM_SIZE too small
@@ -124,7 +131,7 @@ ResetEEPROM() {
124131
EEPROM.end();
125132
}
126133

127-
void
134+
bool
128135
EEPROM_read_string(int start, int count, String & val, String defaultVal = "") {
129136
byte checksum = CHECKSUM_SEED;
130137
for (int i = 0; i < count - 1; ++i) {
@@ -143,7 +150,10 @@ EEPROM_read_string(int start, int count, String & val, String defaultVal = "") {
143150
if(c != checksum) {
144151
DBUGF("Using default '%s'", defaultVal.c_str());
145152
val = defaultVal;
153+
return false;
146154
}
155+
156+
return true;
147157
}
148158

149159
void
@@ -223,8 +233,11 @@ config_load_settings() {
223233
emoncms_fingerprint,"");
224234

225235
// MQTT settings
226-
EEPROM_read_string(EEPROM_MQTT_SERVER_START, EEPROM_MQTT_SERVER_SIZE,
227-
mqtt_server, "emonpi");
236+
if(false == EEPROM_read_string(EEPROM_MQTT_SERVER_START, EEPROM_MQTT_SERVER_SIZE,
237+
mqtt_server, "emonpi")) {
238+
EEPROM_read_string(EEPROM_MQTT_SERVER_V1_START, EEPROM_MQTT_SERVER_V1_SIZE,
239+
mqtt_server, "emonpi");
240+
}
228241
EEPROM_read_string(EEPROM_MQTT_TOPIC_START, EEPROM_MQTT_TOPIC_SIZE,
229242
mqtt_topic, esp_hostname);
230243
EEPROM_read_string(EEPROM_MQTT_USER_START, EEPROM_MQTT_USER_SIZE,
@@ -235,6 +248,7 @@ config_load_settings() {
235248
mqtt_solar);
236249
EEPROM_read_string(EEPROM_MQTT_GRID_IE_START, EEPROM_MQTT_GRID_IE_SIZE,
237250
mqtt_grid_ie, "emon/emonpi/power1");
251+
EEPROM_read_uint24(EEPROM_MQTT_PORT_START, mqtt_port, 1883);
238252

239253
// Web server credentials
240254
EEPROM_read_string(EEPROM_WWW_USER_START, EEPROM_WWW_USER_SIZE,
@@ -296,16 +310,21 @@ config_save_emoncms(bool enable, String server, String node, String apikey,
296310
}
297311

298312
void
299-
config_save_mqtt(bool enable, String server, String topic, String user, String pass, String solar, String grid_ie)
313+
config_save_mqtt(bool enable, int protocol, String server, uint16_t port, String topic, String user, String pass, String solar, String grid_ie, bool reject_unauthorized)
300314
{
301315
EEPROM.begin(EEPROM_SIZE);
302316

303-
flags = flags & ~CONFIG_SERVICE_MQTT;
317+
flags = flags & ~(CONFIG_SERVICE_MQTT | CONFIG_MQTT_PROTOCOL | CONFIG_MQTT_ALLOW_ANY_CERT);
304318
if(enable) {
305319
flags |= CONFIG_SERVICE_MQTT;
306320
}
321+
if(!reject_unauthorized) {
322+
flags |= CONFIG_MQTT_ALLOW_ANY_CERT;
323+
}
324+
flags |= protocol << 4;
307325

308326
mqtt_server = server;
327+
mqtt_port = port;
309328
mqtt_topic = topic;
310329
mqtt_user = user;
311330
mqtt_pass = pass;
@@ -324,6 +343,7 @@ config_save_mqtt(bool enable, String server, String topic, String user, String p
324343
EEPROM_write_string(EEPROM_MQTT_GRID_IE_START, EEPROM_MQTT_GRID_IE_SIZE, mqtt_grid_ie);
325344

326345
EEPROM_write_uint24(EEPROM_FLAGS_START, flags);
346+
EEPROM_write_uint24(EEPROM_MQTT_PORT_START, port);
327347

328348
EEPROM.end();
329349
}

src/app_config.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ extern String emoncms_fingerprint;
3131

3232
// MQTT Settings
3333
extern String mqtt_server;
34+
extern uint32_t mqtt_port;
3435
extern String mqtt_topic;
3536
extern String mqtt_user;
3637
extern String mqtt_pass;
@@ -48,6 +49,8 @@ extern uint32_t flags;
4849
#define CONFIG_SERVICE_MQTT (1 << 1)
4950
#define CONFIG_SERVICE_OHM (1 << 2)
5051
#define CONFIG_SERVICE_SNTP (1 << 3)
52+
#define CONFIG_MQTT_PROTOCOL (7 << 4) // Maybe leave a bit of space after for additional protocols
53+
#define CONFIG_MQTT_ALLOW_ANY_CERT (1 << 7)
5154

5255
inline bool config_emoncms_enabled() {
5356
return CONFIG_SERVICE_EMONCMS == (flags & CONFIG_SERVICE_EMONCMS);
@@ -65,6 +68,14 @@ inline bool config_sntp_enabled() {
6568
return CONFIG_SERVICE_SNTP == (flags & CONFIG_SERVICE_SNTP);
6669
}
6770

71+
inline uint8_t config_mqtt_protocol() {
72+
return (flags & CONFIG_MQTT_PROTOCOL) >> 4;
73+
}
74+
75+
inline bool config_mqtt_reject_unauthorized() {
76+
return 0 == (flags & CONFIG_MQTT_ALLOW_ANY_CERT);
77+
}
78+
6879
// Ohm Connect Settings
6980
extern String ohm;
7081

@@ -81,7 +92,7 @@ extern void config_save_emoncms(bool enable, String server, String node, String
8192
// -------------------------------------------------------------------
8293
// Save the MQTT broker details
8394
// -------------------------------------------------------------------
84-
extern void config_save_mqtt(bool enable, String server, String topic, String user, String pass, String solar, String grid_ie);
95+
extern void config_save_mqtt(bool enable, int protocol, String server, uint16_t port, String topic, String user, String pass, String solar, String grid_ie, bool reject_unauthorized);
8596

8697
// -------------------------------------------------------------------
8798
// Save the admin/web interface details

src/mqtt.cpp

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,17 @@
1616

1717
MongooseMqttClient mqttclient;
1818

19-
long lastMqttReconnectAttempt = 0;
19+
long nextMqttReconnectAttempt = 0;
2020
int clientTimeout = 0;
2121
int i = 0;
2222
bool connecting = false;
2323

2424
String lastWill = "";
2525

26+
#ifndef MQTT_CONNECT_TIMEOUT
27+
#define MQTT_CONNECT_TIMEOUT (5 * 1000)
28+
#endif // !MQTT_CONNECT_TIMEOUT
29+
2630
// -------------------------------------------------------------------
2731
// MQTT msg Received callback function:
2832
// Function to be called when msg is received on MQTT subscribed topic
@@ -102,13 +106,14 @@ mqtt_connect()
102106
connecting = true;
103107

104108
mqttclient.onMessage(mqttmsg_callback); //function to be called when mqtt msg is received on subscribed topic
105-
mqttclient.onError([](uint8_t err) {
106-
DBUGF("MQTT error %u", err);
109+
mqttclient.onError([](int err) {
110+
DBUGF("MQTT error %d", err);
107111
connecting = false;
108112
});
109113

110-
DBUG("MQTT Connecting to...");
111-
DBUGLN(mqtt_server);
114+
String mqtt_host = mqtt_server + ":" + String(mqtt_port);
115+
116+
DBUGF("MQTT Connecting to... %s://%s", MQTT_MQTT == config_mqtt_protocol() ? "mqtt" : "mqtts", mqtt_host.c_str());
112117

113118
// Build the last will message
114119
DynamicJsonDocument willDoc(JSON_OBJECT_SIZE(3) + 60);
@@ -121,9 +126,14 @@ mqtt_connect()
121126
serializeJson(willDoc, lastWill);
122127
DBUGVAR(lastWill);
123128

129+
if(!config_mqtt_reject_unauthorized()) {
130+
DEBUG.println("WARNING: Certificate verification disabled");
131+
}
132+
124133
mqttclient.setCredentials(mqtt_user, mqtt_pass);
125134
mqttclient.setLastWillAndTestimment(mqtt_announce_topic, lastWill, true);
126-
mqttclient.connect(mqtt_server + ":1883", esp_hostname, []()
135+
mqttclient.setRejectUnauthorized(config_mqtt_reject_unauthorized());
136+
connecting = mqttclient.connect((MongooseMqttProtocol)config_mqtt_protocol(), mqtt_host, esp_hostname, []()
127137
{
128138
DBUGLN("MQTT connected");
129139

@@ -230,9 +240,9 @@ mqtt_loop() {
230240

231241
if (!mqttclient.connected()) {
232242
long now = millis();
233-
// try and reconnect continuously for first 5s then try again once every 10s
234-
if ((now < 50000) || ((now - lastMqttReconnectAttempt) > 100000)) {
235-
lastMqttReconnectAttempt = now;
243+
// try and reconnect every x seconds
244+
if (now > nextMqttReconnectAttempt) {
245+
nextMqttReconnectAttempt = now + MQTT_CONNECT_TIMEOUT;
236246
mqtt_connect(); // Attempt to reconnect
237247
}
238248
}
@@ -242,11 +252,10 @@ mqtt_loop() {
242252

243253
void
244254
mqtt_restart() {
245-
// TODO
246-
// if (mqttclient.connected()) {
247-
// mqttclient.disconnect();
248-
// }
249-
// lastMqttReconnectAttempt = 0;
255+
if (mqttclient.connected()) {
256+
mqttclient.disconnect();
257+
}
258+
nextMqttReconnectAttempt = 0;
250259
}
251260

252261
boolean

src/mqtt.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77

88
#include <Arduino.h>
99

10+
#define MQTT_PROTOCOL_MQTT 0
11+
#define MQTT_PROTOCOL_MQTT_SSL 1
12+
#define MQTT_PROTOCOL_WEBSOCKET 2
13+
#define MQTT_PROTOCOL_WEBSOCKET_SSL 3
14+
1015
extern void mqtt_msg_callback();
1116

1217
// -------------------------------------------------------------------

src/root_ca.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const char *root_ca = ""
3333
#endif
3434

3535
#ifndef CA_AMAZON_1
36-
#define CA_AMAZON_1 0
36+
#define CA_AMAZON_1 1
3737
#endif
3838

3939

src/web_server.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,13 +333,33 @@ handleSaveMqtt(MongooseHttpServerRequest *request) {
333333
pass = mqtt_pass;
334334
}
335335

336+
int protocol = MQTT_PROTOCOL_MQTT;
337+
char proto[6];
338+
if(request->getParam("protocol", proto, sizeof(proto)) > 0) {
339+
// Cheap and chearful check, obviously not checking for invalid input
340+
protocol = 's' == proto[4] ? MQTT_PROTOCOL_MQTT_SSL : MQTT_PROTOCOL_MQTT;
341+
}
342+
343+
int port = 1883;
344+
char portStr[8];
345+
if(request->getParam("port", portStr, sizeof(portStr)) > 0) {
346+
port = atoi(portStr);
347+
}
348+
349+
char unauthString[8];
350+
int unauthFound = request->getParam("reject_unauthorized", unauthString, sizeof(unauthString));
351+
bool reject_unauthorized = unauthFound < 0 || 0 == unauthFound || isPositive(String(unauthString));
352+
336353
config_save_mqtt(isPositive(request->getParam("enable")),
354+
protocol,
337355
request->getParam("server"),
356+
port,
338357
request->getParam("topic"),
339358
request->getParam("user"),
340359
pass,
341360
request->getParam("solar"),
342-
request->getParam("grid_ie"));
361+
request->getParam("grid_ie"),
362+
reject_unauthorized);
343363

344364
char tmpStr[200];
345365
snprintf(tmpStr, sizeof(tmpStr), "Saved: %s %s %s %s %s %s", mqtt_server.c_str(),
@@ -652,7 +672,10 @@ handleConfig(MongooseHttpServerRequest *request) {
652672
s += "\",";
653673
s += "\"emoncms_fingerprint\":\"" + emoncms_fingerprint + "\",";
654674
s += "\"mqtt_enabled\":" + String(config_mqtt_enabled() ? "true" : "false") + ",";
675+
s += "\"mqtt_protocol\":\"" + String(0 == config_mqtt_protocol() ? "mqtt" : "mqtts") + "\",";
655676
s += "\"mqtt_server\":\"" + mqtt_server + "\",";
677+
s += "\"mqtt_port\":" + String(mqtt_port) + ",";
678+
s += "\"mqtt_reject_unauthorized\":" + String(config_mqtt_reject_unauthorized() ? "true" : "false") + ",";
656679
s += "\"mqtt_topic\":\"" + mqtt_topic + "\",";
657680
s += "\"mqtt_user\":\"" + mqtt_user + "\",";
658681
s += "\"mqtt_pass\":\"";
@@ -662,7 +685,7 @@ handleConfig(MongooseHttpServerRequest *request) {
662685
s += "\",";
663686
s += "\"mqtt_solar\":\""+mqtt_solar+"\",";
664687
s += "\"mqtt_grid_ie\":\""+mqtt_grid_ie+"\",";
665-
s += "\"mqtt_supported_protocols\":[\"mqtt\"],";
688+
s += "\"mqtt_supported_protocols\":[\"mqtt\",\"mqtts\"],";
666689
s += "\"http_supported_protocols\":[\"http\",\"https\"],";
667690
s += "\"www_username\":\"" + www_username + "\",";
668691
s += "\"www_password\":\"";

0 commit comments

Comments
 (0)