Skip to content

Commit

Permalink
Merge pull request #57 from OpenEVSE/secure_mqtt
Browse files Browse the repository at this point in the history
Secure MQTT support
  • Loading branch information
glynhudson authored Mar 24, 2020
2 parents fd65fba + 6a3edb0 commit 1309b4e
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 30 deletions.
7 changes: 5 additions & 2 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ version = -DBUILD_TAG=3.0.3
monitor_speed = 115200
lib_deps =
ArduinoJson@6.11.1
ArduinoMongoose@0.0.9
ArduinoMongoose@0.0.11
extra_scripts = scripts/extra_script.py
debug_flags =
-DENABLE_DEBUG
Expand All @@ -56,10 +56,12 @@ build_flags =
-DMG_SSL_MBED_DUMMY_RANDOM=1
-DMG_SSL_IF=MG_SSL_IF_MBEDTLS
-DMG_SSL_IF_MBEDTLS_FREE_CERTS=1
-DMG_SSL_IF_MBEDTLS_MAX_FRAG_LEN=2048
#-DMG_SSL_IF_MBEDTLS_MAX_FRAG_LEN=2048
#-DARDUINO_MONGOOSE_DEFAULT_STREAM_BUFFER=2048
#-DARDUINO_MONGOOSE_SEND_BUFFER_SIZE=2048
#-DENABLE_DEBUG
#-DCS_ENABLE_DEBUG
#-DMBEDTLS_DEBUG_C
-DMG_ENABLE_SNTP=1
build_flags_esp8266 =
-DMG_ESP8266
Expand All @@ -77,6 +79,7 @@ src_build_flags_esp32_gateway =
-DRAPI_PORT=Serial2
-DENABLE_WIRED_ETHERNET
-DRANDOM_SEED_CHANNEL=1
monitor_flags = --filter=esp8266_exception_decoder

# specify exact Arduino ESP SDK version, requires platformio 3.5+ (curently dev version)
# http://docs.platformio.org/en/latest/projectconf/section_env_general.html#platform
Expand Down
40 changes: 30 additions & 10 deletions src/app_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ String emoncms_fingerprint = "";

// MQTT Settings
String mqtt_server = "";
uint32_t mqtt_port = 1883;
String mqtt_topic = "";
String mqtt_user = "";
String mqtt_pass = "";
Expand All @@ -47,7 +48,8 @@ uint32_t flags;
#define EEPROM_EMON_API_KEY_SIZE 33
#define EEPROM_EMON_SERVER_SIZE 45
#define EEPROM_EMON_NODE_SIZE 32
#define EEPROM_MQTT_SERVER_SIZE 45
#define EEPROM_MQTT_SERVER_V1_SIZE 45
#define EEPROM_MQTT_SERVER_SIZE 96
#define EEPROM_MQTT_TOPIC_SIZE 32
#define EEPROM_MQTT_USER_SIZE 32
#define EEPROM_MQTT_PASS_SIZE 64
Expand All @@ -59,6 +61,7 @@ uint32_t flags;
#define EEPROM_OHM_KEY_SIZE 10
#define EEPROM_FLAGS_SIZE 4
#define EEPROM_HOSTNAME_SIZE 32
#define EEPROM_MQTT_PORT_SIZE 4
#define EEPROM_SNTP_HOST_SIZE 45
#define EEPROM_TIME_ZONE_SIZE 80
#define EEPROM_SIZE 1024
Expand All @@ -71,9 +74,9 @@ uint32_t flags;
#define EEPROM_EMON_SERVER_END (EEPROM_EMON_SERVER_START + EEPROM_EMON_SERVER_SIZE)
#define EEPROM_EMON_NODE_START EEPROM_EMON_SERVER_END
#define EEPROM_EMON_NODE_END (EEPROM_EMON_NODE_START + EEPROM_EMON_NODE_SIZE)
#define EEPROM_MQTT_SERVER_START EEPROM_EMON_NODE_END
#define EEPROM_MQTT_SERVER_END (EEPROM_MQTT_SERVER_START + EEPROM_MQTT_SERVER_SIZE)
#define EEPROM_MQTT_TOPIC_START EEPROM_MQTT_SERVER_END
#define EEPROM_MQTT_SERVER_V1_START EEPROM_EMON_NODE_END
#define EEPROM_MQTT_SERVER_V1_END (EEPROM_MQTT_SERVER_V1_START + EEPROM_MQTT_SERVER_V1_SIZE)
#define EEPROM_MQTT_TOPIC_START EEPROM_MQTT_SERVER_V1_END
#define EEPROM_MQTT_TOPIC_END (EEPROM_MQTT_TOPIC_START + EEPROM_MQTT_TOPIC_SIZE)
#define EEPROM_MQTT_USER_START EEPROM_MQTT_TOPIC_END
#define EEPROM_MQTT_USER_END (EEPROM_MQTT_USER_START + EEPROM_MQTT_USER_SIZE)
Expand Down Expand Up @@ -101,7 +104,11 @@ uint32_t flags;
#define EEPROM_SNTP_HOST_END (EEPROM_SNTP_HOST_START + EEPROM_SNTP_HOST_SIZE)
#define EEPROM_TIME_ZONE_START EEPROM_SNTP_HOST_END
#define EEPROM_TIME_ZONE_END (EEPROM_TIME_ZONE_START + EEPROM_TIME_ZONE_SIZE)
#define EEPROM_CONFIG_END EEPROM_TIME_ZONE_END
#define EEPROM_MQTT_SERVER_START EEPROM_TIME_ZONE_END
#define EEPROM_MQTT_SERVER_END (EEPROM_MQTT_SERVER_START + EEPROM_MQTT_SERVER_SIZE)
#define EEPROM_MQTT_PORT_START EEPROM_MQTT_SERVER_END
#define EEPROM_MQTT_PORT_END (EEPROM_MQTT_PORT_START + EEPROM_MQTT_PORT_SIZE)
#define EEPROM_CONFIG_END EEPROM_MQTT_PORT_END

#if EEPROM_CONFIG_END > EEPROM_SIZE
#error EEPROM_SIZE too small
Expand All @@ -124,7 +131,7 @@ ResetEEPROM() {
EEPROM.end();
}

void
bool
EEPROM_read_string(int start, int count, String & val, String defaultVal = "") {
byte checksum = CHECKSUM_SEED;
for (int i = 0; i < count - 1; ++i) {
Expand All @@ -143,7 +150,10 @@ EEPROM_read_string(int start, int count, String & val, String defaultVal = "") {
if(c != checksum) {
DBUGF("Using default '%s'", defaultVal.c_str());
val = defaultVal;
return false;
}

return true;
}

void
Expand Down Expand Up @@ -223,8 +233,11 @@ config_load_settings() {
emoncms_fingerprint,"");

// MQTT settings
EEPROM_read_string(EEPROM_MQTT_SERVER_START, EEPROM_MQTT_SERVER_SIZE,
mqtt_server, "emonpi");
if(false == EEPROM_read_string(EEPROM_MQTT_SERVER_START, EEPROM_MQTT_SERVER_SIZE,
mqtt_server, "emonpi")) {
EEPROM_read_string(EEPROM_MQTT_SERVER_V1_START, EEPROM_MQTT_SERVER_V1_SIZE,
mqtt_server, "emonpi");
}
EEPROM_read_string(EEPROM_MQTT_TOPIC_START, EEPROM_MQTT_TOPIC_SIZE,
mqtt_topic, esp_hostname);
EEPROM_read_string(EEPROM_MQTT_USER_START, EEPROM_MQTT_USER_SIZE,
Expand All @@ -235,6 +248,7 @@ config_load_settings() {
mqtt_solar);
EEPROM_read_string(EEPROM_MQTT_GRID_IE_START, EEPROM_MQTT_GRID_IE_SIZE,
mqtt_grid_ie, "emon/emonpi/power1");
EEPROM_read_uint24(EEPROM_MQTT_PORT_START, mqtt_port, 1883);

// Web server credentials
EEPROM_read_string(EEPROM_WWW_USER_START, EEPROM_WWW_USER_SIZE,
Expand Down Expand Up @@ -296,16 +310,21 @@ config_save_emoncms(bool enable, String server, String node, String apikey,
}

void
config_save_mqtt(bool enable, String server, String topic, String user, String pass, String solar, String grid_ie)
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)
{
EEPROM.begin(EEPROM_SIZE);

flags = flags & ~CONFIG_SERVICE_MQTT;
flags = flags & ~(CONFIG_SERVICE_MQTT | CONFIG_MQTT_PROTOCOL | CONFIG_MQTT_ALLOW_ANY_CERT);
if(enable) {
flags |= CONFIG_SERVICE_MQTT;
}
if(!reject_unauthorized) {
flags |= CONFIG_MQTT_ALLOW_ANY_CERT;
}
flags |= protocol << 4;

mqtt_server = server;
mqtt_port = port;
mqtt_topic = topic;
mqtt_user = user;
mqtt_pass = pass;
Expand All @@ -324,6 +343,7 @@ config_save_mqtt(bool enable, String server, String topic, String user, String p
EEPROM_write_string(EEPROM_MQTT_GRID_IE_START, EEPROM_MQTT_GRID_IE_SIZE, mqtt_grid_ie);

EEPROM_write_uint24(EEPROM_FLAGS_START, flags);
EEPROM_write_uint24(EEPROM_MQTT_PORT_START, port);

EEPROM.end();
}
Expand Down
13 changes: 12 additions & 1 deletion src/app_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ extern String emoncms_fingerprint;

// MQTT Settings
extern String mqtt_server;
extern uint32_t mqtt_port;
extern String mqtt_topic;
extern String mqtt_user;
extern String mqtt_pass;
Expand All @@ -48,6 +49,8 @@ extern uint32_t flags;
#define CONFIG_SERVICE_MQTT (1 << 1)
#define CONFIG_SERVICE_OHM (1 << 2)
#define CONFIG_SERVICE_SNTP (1 << 3)
#define CONFIG_MQTT_PROTOCOL (7 << 4) // Maybe leave a bit of space after for additional protocols
#define CONFIG_MQTT_ALLOW_ANY_CERT (1 << 7)

inline bool config_emoncms_enabled() {
return CONFIG_SERVICE_EMONCMS == (flags & CONFIG_SERVICE_EMONCMS);
Expand All @@ -65,6 +68,14 @@ inline bool config_sntp_enabled() {
return CONFIG_SERVICE_SNTP == (flags & CONFIG_SERVICE_SNTP);
}

inline uint8_t config_mqtt_protocol() {
return (flags & CONFIG_MQTT_PROTOCOL) >> 4;
}

inline bool config_mqtt_reject_unauthorized() {
return 0 == (flags & CONFIG_MQTT_ALLOW_ANY_CERT);
}

// Ohm Connect Settings
extern String ohm;

Expand All @@ -81,7 +92,7 @@ extern void config_save_emoncms(bool enable, String server, String node, String
// -------------------------------------------------------------------
// Save the MQTT broker details
// -------------------------------------------------------------------
extern void config_save_mqtt(bool enable, String server, String topic, String user, String pass, String solar, String grid_ie);
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);

// -------------------------------------------------------------------
// Save the admin/web interface details
Expand Down
37 changes: 23 additions & 14 deletions src/mqtt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@

MongooseMqttClient mqttclient;

long lastMqttReconnectAttempt = 0;
long nextMqttReconnectAttempt = 0;
int clientTimeout = 0;
int i = 0;
bool connecting = false;

String lastWill = "";

#ifndef MQTT_CONNECT_TIMEOUT
#define MQTT_CONNECT_TIMEOUT (5 * 1000)
#endif // !MQTT_CONNECT_TIMEOUT

// -------------------------------------------------------------------
// MQTT msg Received callback function:
// Function to be called when msg is received on MQTT subscribed topic
Expand Down Expand Up @@ -102,13 +106,14 @@ mqtt_connect()
connecting = true;

mqttclient.onMessage(mqttmsg_callback); //function to be called when mqtt msg is received on subscribed topic
mqttclient.onError([](uint8_t err) {
DBUGF("MQTT error %u", err);
mqttclient.onError([](int err) {
DBUGF("MQTT error %d", err);
connecting = false;
});

DBUG("MQTT Connecting to...");
DBUGLN(mqtt_server);
String mqtt_host = mqtt_server + ":" + String(mqtt_port);

DBUGF("MQTT Connecting to... %s://%s", MQTT_MQTT == config_mqtt_protocol() ? "mqtt" : "mqtts", mqtt_host.c_str());

// Build the last will message
DynamicJsonDocument willDoc(JSON_OBJECT_SIZE(3) + 60);
Expand All @@ -121,9 +126,14 @@ mqtt_connect()
serializeJson(willDoc, lastWill);
DBUGVAR(lastWill);

if(!config_mqtt_reject_unauthorized()) {
DEBUG.println("WARNING: Certificate verification disabled");
}

mqttclient.setCredentials(mqtt_user, mqtt_pass);
mqttclient.setLastWillAndTestimment(mqtt_announce_topic, lastWill, true);
mqttclient.connect(mqtt_server + ":1883", esp_hostname, []()
mqttclient.setRejectUnauthorized(config_mqtt_reject_unauthorized());
connecting = mqttclient.connect((MongooseMqttProtocol)config_mqtt_protocol(), mqtt_host, esp_hostname, []()
{
DBUGLN("MQTT connected");

Expand Down Expand Up @@ -230,9 +240,9 @@ mqtt_loop() {

if (!mqttclient.connected()) {
long now = millis();
// try and reconnect continuously for first 5s then try again once every 10s
if ((now < 50000) || ((now - lastMqttReconnectAttempt) > 100000)) {
lastMqttReconnectAttempt = now;
// try and reconnect every x seconds
if (now > nextMqttReconnectAttempt) {
nextMqttReconnectAttempt = now + MQTT_CONNECT_TIMEOUT;
mqtt_connect(); // Attempt to reconnect
}
}
Expand All @@ -242,11 +252,10 @@ mqtt_loop() {

void
mqtt_restart() {
// TODO
// if (mqttclient.connected()) {
// mqttclient.disconnect();
// }
// lastMqttReconnectAttempt = 0;
if (mqttclient.connected()) {
mqttclient.disconnect();
}
nextMqttReconnectAttempt = 0;
}

boolean
Expand Down
5 changes: 5 additions & 0 deletions src/mqtt.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

#include <Arduino.h>

#define MQTT_PROTOCOL_MQTT 0
#define MQTT_PROTOCOL_MQTT_SSL 1
#define MQTT_PROTOCOL_WEBSOCKET 2
#define MQTT_PROTOCOL_WEBSOCKET_SSL 3

extern void mqtt_msg_callback();

// -------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion src/root_ca.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const char *root_ca = ""
#endif

#ifndef CA_AMAZON_1
#define CA_AMAZON_1 0
#define CA_AMAZON_1 1
#endif


Expand Down
27 changes: 25 additions & 2 deletions src/web_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,13 +333,33 @@ handleSaveMqtt(MongooseHttpServerRequest *request) {
pass = mqtt_pass;
}

int protocol = MQTT_PROTOCOL_MQTT;
char proto[6];
if(request->getParam("protocol", proto, sizeof(proto)) > 0) {
// Cheap and chearful check, obviously not checking for invalid input
protocol = 's' == proto[4] ? MQTT_PROTOCOL_MQTT_SSL : MQTT_PROTOCOL_MQTT;
}

int port = 1883;
char portStr[8];
if(request->getParam("port", portStr, sizeof(portStr)) > 0) {
port = atoi(portStr);
}

char unauthString[8];
int unauthFound = request->getParam("reject_unauthorized", unauthString, sizeof(unauthString));
bool reject_unauthorized = unauthFound < 0 || 0 == unauthFound || isPositive(String(unauthString));

config_save_mqtt(isPositive(request->getParam("enable")),
protocol,
request->getParam("server"),
port,
request->getParam("topic"),
request->getParam("user"),
pass,
request->getParam("solar"),
request->getParam("grid_ie"));
request->getParam("grid_ie"),
reject_unauthorized);

char tmpStr[200];
snprintf(tmpStr, sizeof(tmpStr), "Saved: %s %s %s %s %s %s", mqtt_server.c_str(),
Expand Down Expand Up @@ -652,7 +672,10 @@ handleConfig(MongooseHttpServerRequest *request) {
s += "\",";
s += "\"emoncms_fingerprint\":\"" + emoncms_fingerprint + "\",";
s += "\"mqtt_enabled\":" + String(config_mqtt_enabled() ? "true" : "false") + ",";
s += "\"mqtt_protocol\":\"" + String(0 == config_mqtt_protocol() ? "mqtt" : "mqtts") + "\",";
s += "\"mqtt_server\":\"" + mqtt_server + "\",";
s += "\"mqtt_port\":" + String(mqtt_port) + ",";
s += "\"mqtt_reject_unauthorized\":" + String(config_mqtt_reject_unauthorized() ? "true" : "false") + ",";
s += "\"mqtt_topic\":\"" + mqtt_topic + "\",";
s += "\"mqtt_user\":\"" + mqtt_user + "\",";
s += "\"mqtt_pass\":\"";
Expand All @@ -662,7 +685,7 @@ handleConfig(MongooseHttpServerRequest *request) {
s += "\",";
s += "\"mqtt_solar\":\""+mqtt_solar+"\",";
s += "\"mqtt_grid_ie\":\""+mqtt_grid_ie+"\",";
s += "\"mqtt_supported_protocols\":[\"mqtt\"],";
s += "\"mqtt_supported_protocols\":[\"mqtt\",\"mqtts\"],";
s += "\"http_supported_protocols\":[\"http\",\"https\"],";
s += "\"www_username\":\"" + www_username + "\",";
s += "\"www_password\":\"";
Expand Down

0 comments on commit 1309b4e

Please sign in to comment.