ESP32(M5Stack Basic)上でWireGuardを使用したMQTT実装サンプル

このエントリーは[NGINXのStreamモジュールでMQTT <=> MQTTS変換をしてAWS IoT Coreへ接続する](https://qiita.com/ma2shita/items/e0b83868bf64834f3bc6)における、MCUとProxy間を WireGuard で保護するアーキテクチャの解説です。

## ESP32(M5Stack Basic)上でWireGuardを使用したMQTT実装サンプル

```cpp:mqtt_on_wireguard.ino
/*
   Copyright 2021 Kohei "Max" MATSUSHITA.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
#include <M5Stack.h>

#include <WiFi.h>
char WIFI_SSID[] = "{YOUR_WIFI_SSID}";
char WIFI_PASSWORD[] = "{YOUR_WIFI_PASS}";
WiFiClient net;

#include <WireGuard-ESP32.h>
static WireGuard wg;
char private_key[] = "{YOUR [Interface] PrivateKey}"; // [Interface] PrivateKey
IPAddress local_ip(192, 168, 200, 254);               // [Interface] Address
char public_key[] = "{YOUR [Peer] PublicKey}";        // [Peer] PublicKey
char endpoint_address[] = "{YOUR [Peer] Endpoint}";   // [Peer] Endpoint
int endpoint_port = 11010;                            // [Peer] Endpoint

#include <PubSubClient.h>
PubSubClient mqtt = PubSubClient(net);

#define THING_NAME      "M5StackBasic0"
#define ENDPOINT        "beam.soracom.io"
#define PORT             (1883)
#define PUBLISH_TOPIC   "demo0/via_beam"

void connectToNetwork() {
    Serial.print("Connecting to Wi-Fi: ");
    WiFi.mode(WIFI_STA);
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    while (WiFi.status() != WL_CONNECTED){
        delay(200);
        Serial.print(".");
    }
    Serial.println("done.");
}

void connectToWireGuard() {
    Serial.print("Adjusting system time: ");
    configTime(9 * 60 * 60, 0, "ntp.jst.mfeed.ad.jp", "ntp.nict.jp", "time.google.com");
    delay(3000); // Wait for adjust
    Serial.println("done.");
    Serial.print("Connect to SORACOM Arc (WireGuard):");
    wg.begin(local_ip, private_key, endpoint_address, public_key, endpoint_port);
    Serial.println("done.");
}

void connectToMQTTS() {
    Serial.print("Connecting to MQTTS broker via SORACOM Beam(Proxy): ");
    mqtt.setServer(ENDPOINT, PORT);
    while (!mqtt.connect(THING_NAME)) {
        Serial.print(".");
        delay(200);
    }
    Serial.println("Connected!");
}

void setup() {
    Serial.begin(115200);
    connectToNetwork();
    connectToWireGuard();
    connectToMQTTS();
}

void publishMessage() {
    char payload[512];
    sprintf(payload, "{\"time\": %lu}", millis());
    mqtt.publish(PUBLISH_TOPIC, payload);
}

#define LOOP_INTERVAL_MS (10000)
void loop() {
    Serial.println("loop");
    publishMessage();
    unsigned long next = millis();
    while (millis() < next + LOOP_INTERVAL_MS) {
        mqtt.loop();
    }
}
```

## 利用可能なサーバー側の構成

サーバー上にWireGuardサーバーとNGINXによるMQTT/MQTTS中継サーバーを構築した環境、もしくはSORACOM ArcとSORACOM Beamの組み合わせで利用できます。

SORACOM Arcはいわゆる「マネージドWireGuardサーバー」サービス、SORACOM BeamはMQTT等のプロトコル変換/転送サービスです。

#### 独自でサーバーを構築する場合

```plantuml
"M5Stack(ESP32)" -> WireGuardサーバー: WireGuard
"M5Stack(ESP32)" -> NGINX: MQTT
NGINX -> NGINX: MQTT→MQTTS
box "サーバー"
participant WireGuardサーバー
participant NGINX
end box
NGINX -> MQTTSブローカー: MQTTS
```

#### SORACOM プラットフォームを利用する場合

```plantuml
"M5Stack(ESP32)" -> "SORACOM Arc": WireGuard
"M5Stack(ESP32)" -> "SORACOM Beam": MQTT
"SORACOM Beam" -> "SORACOM Beam": MQTT→MQTTS
box "SORACOM プラットフォーム" #33CDD7
participant "SORACOM Arc"
participant "SORACOM Beam"
end box
"SORACOM Beam" -> MQTTSブローカー: MQTTS
```

独自でサーバーを構築する場合のNGINX設定は[NGINXのStreamモジュールでMQTT <=> MQTTS変換をしてAWS IoT Coreへ接続する](https://qiita.com/ma2shita/items/e0b83868bf64834f3bc6)をご覧ください。
WireGuardについては公式のマニュアルをご確認ください。

SORACOM プラットフォームについては[SORACOM Arcのユーザードキュメント](https://users.soracom.io/ja-jp/docs/arc/)並びに[SORACOM Beamのユーザードキュメント](https://users.soracom.io/ja-jp/docs/beam/)をご覧ください。

## フットプリント比較

同等の実装でMQTT+TLS、MQTT+WireGuard、MQTTのみでフットプリントを比較しました。

|        | フラッシュメモリ | RAM |
|--------|----------:|---------:|
| [MQTT+TLS](https://qiita.com/ma2shita/items/e0b83868bf64834f3bc6#%E3%83%A1%E3%83%A2%E3%83%AA%E3%83%BC%E3%81%AE%E4%BD%BF%E7%94%A8%E9%87%8F) | 911,762 | 40,456 |
| MQTT+WireGuard      | 779,158 |41,120 |
| [MQTTのみ](https://qiita.com/ma2shita/items/e0b83868bf64834f3bc6#mcu--m5stack-basic%E5%81%B4)     |748,302 | 39,912 |

MQTT+WireGuardは、MQTT+TLSと比較してフラッシュメモリを 141KBも節約できます。MQTTのみから+30KBでVPNが張れるとも言えます。

#### 確認環境

* Arduino IDE 1.18.6
* M5Stack Basic
    * ボード定義: ESP32(1.0.6)
    * ライブラリ: M5Stack(0.3.6)

## あとがき






EoT