DockerでMosquitteを立ててAWS IoTのMQTTトピックにPublishをしてみる

こんにちは、CX事業本部の若槻です。

AWS IoT上のMQTTトピックに対してDockerコンテナ上のMosquitteからメッセージをpublishする検証をしてみたのでご紹介します。

Eclipse Mosquitto is an open source (EPL/EDL licensed) message broker that implements the MQTT protocol versions 5.0, 3.1.1 and 3.1. Mosquitto is lightweight and is suitable for use on all devices from low power single board computers to full servers.

The MQTT protocol provides a lightweight method of carrying out messaging using a publish/subscribe model. This makes it suitable for Internet of Things messaging such as with low power sensors or mobile devices such as phones, embedded computers or microcontrollers.

章概要

  • やってみる
    • AWS IoT環境準備
    • DockerコンテナでMosquitteを立てる
    • AWS IoTのMQTTトピック/demo/eventに対するサブスクリプションを作成する
    • AWS IoTのMQTTトピック/demo/eventにPublishする
  • 補足
    • IoTポリシーとクライアントIDについて
    • openSUSEからのmosquitteの取得について
  • おわりに
  • 参考

やってみる

AWS IoT環境準備

まずはAWS IoTの環境準備を行っていきます。

1. certsディレクトリの作成

certsディレクトリを作成します。このディレクトリはMosquitteを立てるDockerコンテナからマウントして利用します。

$ mkdir ./certs

2. キーペアとデバイス証明書の作成

AWS CLIで以下のコマンドを実行し、2048ビットのRSAキーペアの作成と、発行された公開鍵を使用してX.509のデバイス証明書の発行を行います。作成したキーと証明書はcertsディレクトリに格納します。

$ aws iot create-keys-and-certificate \
  --set-as-active \
  --public-key-outfile ./certs/publicKey.pem \
  --private-key-outfile ./certs/privateKey.pem \
  --certificate-pem-outfile ./certs/cert.pem
{
    "certificateArn": "arn:aws:iot:ap-northeast-1:123456789012:cert/00c0479e991dcfa918d67b53762ecd95e1171c7ee984f44399d86a8c581ba817",
    "certificateId": "00c0479e991dcfa918d67b53762ecd95e1171c7ee984f44399d86a8c581ba817",
    "certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDWTCCAkGgAwIBAgIULkFh/IUQ6QlCW/2YEIyjihut0FswDQYJKoZIhvcNAQEL\n(中略)-----END CERTIFICATE-----\n",
    "keyPair": {
        "PublicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmWgt6TbmhVXxnr9mS+hw\n(中略)-----END PUBLIC KEY-----\n",
        "PrivateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAmWgt6TbmhVXxnr9mS+hwXVz4//994SNPzY43rLb3us0hEWse\n(中略)-----END RSA PRIVATE KEY-----\n"
    }
}

3. ルートCA証明書の取得

以下のコマンドを実行し、デバイスがAWS IoTをサーバー認証するためのルートCA証明書をAmazonトラストサービスのリポジトリからダウンロードしてcertsディレクトリに保存します。

$ curl -o ./certs/rootCA.pem \
  -s https://www.amazontrust.com/repository/AmazonRootCA1.pem

4. IoTポリシーの作成

以下のdemo-policy.jsonファイルを作成します。

$ cat demo-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:Connect",
        "iot:Publish"
      ],
      "Resource": [
        "arn:aws:iot:ap-northeast-1:123456789012:client/demo-device-id-1",
        "arn:aws:iot:ap-northeast-1:123456789012:topic//demo/event"
      ]
    }
  ]
}

AWS CLIで以下のコマンドを実行し、IoTポリシーdemoPolicyを作成します。

$ aws iot create-policy \
   --policy-name demoPolicy \
   --policy-document file://demo-policy.json

5. IoTポリシーをデバイス証明書へアタッチ

AWS CLIで以下のコマンドを実行し、IoTポリシーdemoPolicyをデバイス証明書にアタッチします。ここで--principalに指定するのは、iot create-keys-and-certificateコマンド実行結果のcertificateArnの値です。

$ aws iot attach-principal-policy \
  --policy-name demoPolicy \
  --principal arn:aws:iot:ap-northeast-1:123456789012:cert/00c0479e991dcfa918d67b53762ecd95e1171c7ee984f44399d86a8c581ba817

これでAWS IoTの環境準備ができました。(今回はMQTTトピックにpublishをするだけなのでモノの作成は不要です)

DockerコンテナでMosquitteを立てる

mosquitto公式提供のDockerイメージeclipse-mosquittoからコンテナを起動します。

$ docker run --name mosquitto-demo -v "${PWD}"/certs:/certs -itd eclipse-mosquitto:latest

AWS IoTのMQTTトピック/demo/eventに対するサブスクリプションを作成する

AWS IoTのマネージドコンソールの[テスト]を開きます。[トピックへサブスクライブする]を選択して[トピックのサブスクリプション]にトピック名/demo/eventを入力し、[トピックへのサブスクライブ]をクリックします。

image.png

/demo/eventに対するサブスクリプションが作成されます。

image.png

AWS IoTのMQTTトピック/demo/eventにPublishする

AWS CLIで以下のコマンドを実行して、Dockerコンテナ上のmosquittoからMQTTトピック/demo/eventに対してメッセージをpublishします。

$ docker exec -it mosquitto-demo /bin/ash -c "mosquitto_pub \
  -h $(aws iot describe-endpoint --endpoint-type iot:Data-ATS | jq -r .endpointAddress) \
  -p 8883 \
  -q 1 \
  -i demo-device-id-1 \
  -m '{\"message\":\"こんばんは!!!\"}' \
  -t /demo/event \
  --cafile ./certs/rootCA.pem \
  --key ./certs/privateKey.pem \
  --cert ./certs/cert.pem"

先程AWSコンソールにて作成したサブスクリプションにて、/demo/eventトピックに対してpublishしたメッセージ{"message": "こんばんは!!!"}がsubscribeできていることが確認できました。

image.png

補足

openSUSEからのmosquitteの取得について

多くの記事では、以下のopenSUSEのURLよりMosquitteクライアントのダウンロードが可能であると紹介されていますが、2020年1月時点では404エラーとなり利用できないようです。

  • http://download.opensuse.org/repositories/home:/oojah:/mqtt/CentOS_CentOS-6/home:oojah:mqtt.repo

image.png

よって、当たり前ですが、Amazon LinuxやCent OSへ以下のようなコマンドでMosquitteをインストールしようとしても、404エラーのページしかダウンロードできないためyum installは失敗します。

$ sudo curl http://download.opensuse.org/repositories/home:/oojah:/mqtt/CentOS_CentOS-6/home:oojah:mqtt.repo -o /etc/yum.repos.d/mqtt.repo
$ sudo yum install -y mosquitto-clients
Loaded plugins: priorities, update-motd, upgrade-helper


File contains no section headers.
file: file:///etc/yum.repos.d/mqtt.repo, line: 1
'<?xml version="1.0" encoding="UTF-8"?>\n'

そこで私は、Amazon Linux上でMosquitteを利用するために今回紹介したようにDockerでMosquitteを立てる必要があった、という経緯がありました。

IoTポリシーとクライアントIDについて

publish時(mosquitto_pubコマンド実行時)に-iオプションで指定するクライアントIDと、同リクエスト時に送信されるデバイス証明書にアタッチされるIoTポリシーで許可されるクライアントIDリソースがどのような組み合わせパターンのときにどのような動作となるか整理しました。

パターン1

  • mosquitto_pubコマンドでの指定:demo-device-id-1
  • IoTポリシーでの許可:demo-device-id-1

本記事[やってみた]にてご紹介したパターンです。トピックへのpublish時に指定したクライアントID(demo-device-id-1)がIoTポリシーで許可されているため、publish可能となります。

  • IoTポリシー
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:Connect",
        "iot:Publish"
      ],
      "Resource": [
        "arn:aws:iot:ap-northeast-1:123456789012:client/demo-device-id-1",
        "arn:aws:iot:ap-northeast-1:123456789012:topic//demo/event"
      ]
    }
  ]
}
  • mosquitto_pubコマンドの実行
$ docker exec -it mqtt /bin/ash -c "mosquitto_pub \
   -h $(aws iot describe-endpoint --endpoint-type iot:Data-ATS | jq -r .endpointAddress) \
   -p 8883 \
   -q 1 \
   -i demo-device-id-1 \
   -m '{\"message\":\"こんばんは\"}' \
   -t /demo/event \
   --cafile ./certs/rootCA.pem \
   --key ./certs/privateKey.pem \
   --cert ./certs/cert.pem"

パターン2

  • mosquitto_pubコマンドでの指定:demo-device-id-any
  • IoTポリシーでの許可:demo-device-id-1

トピックへのpublish時に指定したクライアントID(demo-device-id-any)がIoTポリシーで許可されていないため、publish不可となります。

  • IoTポリシー
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:Connect",
        "iot:Publish"
      ],
      "Resource": [
        "arn:aws:iot:ap-northeast-1:123456789012:client/demo-device-id-1",
        "arn:aws:iot:ap-northeast-1:123456789012:topic//demo/event"
      ]
    }
  ]
}
  • mosquitto_pubコマンドの実行
$ docker exec -it mqtt /bin/ash -c "mosquitto_pub \
   -h $(aws iot describe-endpoint --endpoint-type iot:Data-ATS | jq -r .endpointAddress) \
   -p 8883 \
   -q 1 \
   -i demo-device-id-any \
   -m '{\"message\":\"こんばんは\"}' \
   -t /demo/event \
   --cafile ./certs/rootCA.pem \
   --key ./certs/privateKey.pem \
   --cert ./certs/cert.pem"
Error: The connection was lost.

パターン3

  • mosquitto_pubコマンドでの指定:demo-device-id-any
  • IoTポリシーでの許可:*

IoTポリシーでワイルドカード*により任意のクライアントIDが許可されているため、任意のクライアントID(demo-device-id-anyなど)を指定してpublish可能となります。

  • IoTポリシー
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:Connect",
        "iot:Publish"
      ],
      "Resource": [
        "arn:aws:iot:ap-northeast-1:123456789012:client/*",
        "arn:aws:iot:ap-northeast-1:123456789012:topic//demo/event"
      ]
    }
  ]
}
  • mosquitto_pubコマンドの実行
$ docker exec -it mqtt /bin/ash -c "mosquitto_pub \
  -h $(aws iot describe-endpoint --endpoint-type iot:Data-ATS | jq -r .endpointAddress) \
  -p 8883 \
  -q 1 \
  -i demo-device-id-any \
  -m '{\"message\":\"こんばんは\"}' \
  -t /demo/event \
  --cafile ./certs/rootCA.pem \
  --key ./certs/privateKey.pem \
  --cert ./certs/cert.pem"

おわりに

AWS IoT上のMQTTトピックに対してDockerコンテナ上のMosquitteからメッセージをpublishしてみました。AWS IoT側の環境準備さえできていれば「コンテナ起動→コンテナ上でmosquitto_pubコマンド実行」の2ステップでMQTTトピックにpublishが行えるため、ちょっとしたトピック発行の検証をしたい時に重宝しそうです。参考になれば幸いです。

参考