まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

2015/07

以前のブログエントリでオープンソース MQTT 環境である Mosquitto の導入方法を紹介しました。このブログエントリではラズベリーパイ用と CentOS 用の紹介をしていますが、Mosquitto 自体には Windows などのバイナリも用意されていて、多くの環境で使えます:
ラズベリーパイにオープンソース MQTT の Mosquitto をインストールする


この Mosquitto を使って、IBM IoTF(IoT Foundations) 環境に用意されている QuickStart と呼ばれる MQTT ブローカーに任意のメッセージデータを送る方法を紹介します。センサーシミュレータなどを使って QuickStart に送られたデータを集めて取り出して加工して・・・という作業は Node-RED 環境があれば簡単ですが、その前段になるデバイスから QuickStart にデータを送るにはどうするか?という手段の紹介です。今回は MQTT クライアント(パブリッシャー)を使った例を紹介します。


準備として、まずは上記のリンク先の情報から、Mosquitto のパブリッシャー(mosquitto_pub)をインストールする所までは済ませておいてください。今回は mosquitto_pub を使って IoTF QuickStart にデータを送信します。

次に、IoTF QuickStart に送られてきたメッセージデータを確認するための環境を整えておく必要があります。IBM Bluemix の IoT Foundations Starter ボイラープレートや Node-RED Starter ボイラープレートを使って、Node-RED 環境を用意してください。この辺りの詳しい手順がよく分からない場合はこちらを参照してください:


Node-RED 環境ができたら、次のような IBM IoT ノードと Debug ノードをつなげただけの、シンプルなフローを作成してください:
2015072701


IBM IoT ノードをダブルクリックし、このような属性値に設定します。Authentication を "QuickStart" に、Input Type を "Device Event" に、そして Device Id には任意のユニークな文字列(下の例では "91a19d112233" にしていますが、同じものを使わないでください。実際にはネットに接続された機器の MAC アドレスを想定しています)を入力します。最後に Name に適当な名前を入力して OK をクリックします:
2015072702


Debug ノードの属性は以下のようにします。Output はデフォルトの payload のままでも実用的にはいいのですが、今回は送られてくるメッセージ全体を確認したいので "complete msg object" に変更して OK をクリックします。最後に "Deploy" をクリックして、デプロイまで済ませておきます:
2015072703


これで Device Id が(今回の例であれば) "91a19d112233" に設定されたデバイスから QuickStart に送られてくるデータを取得してデバッグタブに表示する、というアプリが動いている状態になりました。

では Mosquitto を使って、そのようなデータを QuickStart にパブリッシュしてみます。Mosquitto を導入した環境にログインし、ターミナルのプロンプトから以下のようなコマンドを実行します:
$ mosquitto_pub -h quickstart.messaging.internetofthings.ibmcloud.com -t "iot-2/evt/myeventtype/fmt/json" -m '{"d":{"name1":"stringValue","name2":10}}' -i d:quickstart:MyDevice:91a19d112233

このコマンドは、
 (1) quickstart.messaging.internetofthings.ibmcloud.com という MQTT ブローカーに対して、
 (2) "iot-2/evt/myeventtype/fmt/json" というトピックを指定し、
 (3) '{ "d": { "name1":"stringValue", "name2": 10 } }' という JSON 形式のメッセージを、
 (4) d:quickstart:MyDevice:91a19d112233 というクライアント ID で
パブリッシュする、という処理内容のコマンドです。

※ちなみに IoTF では送信メッセージは '{ "d": { (ここが実際の送信内容) } }' という JSON 形式で送付することを推奨しています。

2015072704


このコマンドを実行した直後、Node-RED アプリのデバッグタブにメッセージが表示されるはずです! 実行したパブリッシュコマンドのメッセージが届いた証拠です:
2015072705


ここで届いたメッセージはこのような内容になっているはずです:
{
 "topic": "iot-2/type/MyDevice/id/91a19d112233/evt/myeventtype/fmt/json",
 "payload": {
  "d": {
   "name1": "stringValue",
   "name2": 10
  }
 },
 "deviceId": "91a19d112233",
 "deviceType": "MyDevice",
 "eventType": "myeventtype",
 "format": "json",
 "_msgid": "6e152038.91eae" 
}

(3) で指定したメッセージは、"payload" の中身として届けられています。また (2) で指定したトピックは "topic" と、"eventType" の値として表示されています。また (4) で指定したクライアント ID は "deviceId""deviceType" として届いていることもわかります。"deviceType" と "eventType" はパブリッシュする側で任意に設定できる、デバイスとイベントの分類用文字列です。


ということは、上記の mosquitto_pub コマンドで実行した MQTT パブリッシュ処理に相当するコマンドを quickstart.messaging.internetofthings.ibmcloud.com に対して実行することで QuickStart にメッセージを送ることができる、ということが分かりました。QuickStart にメッセージを送ることができれば、後は Node-RED を使って便利に処理できます。

このコマンドをプログラミング言語から実行するにはどのようにすればよいのか、それは別途紹介させていただくつもりです。
 

自宅サーバー派の人は、独自ドメインを所有して使っているケースも多いと思います。自分もその一人です。

自宅サーバーで独自ドメインを使う場合、その多くはダイナミック DNS を使うことになると思っています。自宅のネットワーク環境は固定 IP アドレスではなく、大抵動的 IP アドレスであり、この割当はいつ変わるかわかりません。つまり固定の DNS で一度設定しても、IP アドレスの割り当てが変わってしまうと、再度新しい設定を DNS サーバーに知らせるまでは使えなくなってしまいます。そのため DNS を固定ではなく、動的に変更する前提で対応できるよう、ダイナミック DNS サービスを使うケースが大半になります。

これまで自分はダイナミック DNS 用の専用のクライアントアプリをインストールして、自分のアカウントを設定して、それを定期的に実行するよう設定して・・・ と面倒な手続きをしていました。 が、ダイナミック DNS サービスとして MyDNS.jp を使っている場合であれば、普通に Linux の cron でコマンドを実行するだけで定期的な IP アドレス更新ができることが分かりました。


具体的にはこんな感じです:
# crontab -e

(以下の1行を追加して保存)
*/10 * * * * wget -q -O /dev/null http://(mydns.jp のマスターID):(mydns.jp のパスワード)@www.mydns.jp/login.html


MyDNS.jp のアカウント(マスターIDとパスワード)を持っていることが前提になりますが、それらの情報をセットした URL に対して10分おきに HTTP GET リクエストを発行する、という内容のコマンドを指定しています。これであっさりと繋がってしまいました。。

自分の場合は、これを自宅ネットに接続したラズベリーパイに設定しています。自宅のラズベリーパイが自宅ネットワークのダイナミック DNS を定期的に設定してくれるクライアントとしての役目を果たしている、ということになります。なかなかいいラズベリーパイの使い方だと思ってます。


FireFox を使っていて https のサイトを訪れた時に「安全な接続ができませんでした」というエラーページに出くわすことがあります。それも選択肢が「再試行」と「報告」しかなくて、例外追加みたいな対応もできないケースです:
2015072501


よく見ると SSL が安全ではない、みたいなメッセージも書かれてます。この件に関する Mozzila としての見解はこちらです。昨年ですが、通称「プードルアタック」と呼ばれる脆弱性が SSL v3 に見つかり、SSL v3 は安全ではなくなってしまいました。その対策としてデフォルトでこのプロトコルを使ったサイトをブロックします、とのことです:
https://support.mozilla.org/ja/kb/enable-ssl-fix-cannot-connect-securely-error


まあ何もしないのはまずいと思うし、Mozzila の言いぶんはわかる。利用者には罪はなくて、サイトを提供する側に(安全ではない)SSL v3 を使わないでほしい、ということだと思っています。

とは言っても、すぐに対応できるとは限らないケースもあります。例えばサーバーイメージが仮想的に提供されているような場合は、その仮想イメージで SSL v3 を使っていることもあります。仮想イメージをすぐに更新してほしいと言われても困るし、仮に仮想イメージそのものは更新できたとしても既に仮想環境内で使われているイメージをどうやって更新するか、という問題だってあります。要はサイト提供側だけにこの問題の責任を追わせるのには無理があるのかな・・・と。

では諦めるしかないのか、というとそんなこともありません。ブラウザで利用するユーザーの自己責任にはなりますが、このような SSL v3 のサイトにアクセスするように FireFox の設定を変更することもできます。以下にその方法を紹介しますが、SSLv3 が安全ではないことに変わりはないので、社内の環境など、本当に安全が保証されているサイトでのみ、また目的のサイトへのアクセスが終わったらすぐに設定を元に戻るなど、自己責任で使ってください。

ではその設定方法を紹介します。まず FireFox を起動して、アドレス欄に about:config と入力します。「動作保証外である」という警告メッセージが表示されますが、「細心の注意を払って使用する」ボタンをクリックします:
2015072502


すると FireFox の挙動に関わる各種設定項目の一覧が表示されます。かなり大量にあるので、ここから目的の項目を探すのは大変なので、検索をします:
2015072503


「検索」と書かれたフィールドに "security.ssl3.dhe" と入力すると、少しずつ項目が絞られていきながら、ここまで入力した時点で2つの項目だけが表示されているはずです。またどちらも初期設定値の true が設定されているはずです:
2015072504


これら2行の項目の設定値を false に変更します。変更するには各行をダブルクリックすることで変更できます:
2015072505


この状態で先程エラーに成ったページに再度アクセスすると、ちゃんと見れるようになるはずです。



繰り返しになりますが、この設定状態は必ずしも安全ではないため、目的のページを見終わった後は、2箇所とも設定を元通りに戻しておくことを強くおすすめします:
2015072506


ちなみにプードルアタックとその対策についてはこちらで詳しく書かれていたので、是非参照してください:
SSLv3 の脆弱性 POODLE への対策を行う


JavaScript にちょっとした工夫をすると、サブドメインの異なる2つのサイト(例えば www1.mydomain.com と www2.mydomain.com)でクッキーを共有することができる、らしい!

例えばこんな感じです。まずは http://www1.mydomain.com/setCookie.html でクッキーをセットします:
(setCookie.html)

<html>
<head>
<script type="text/javascript">
function setMyCookie(username, password) {
  var s=setMyCookieWithDomain("username",username);
  document.cookie = s;
  var t=setMyCookieWithDomain("password",password);
  document.cookie = t;
  alert(s+"\n"+t); // 保存内容を確認表示
}
function setMyCookieWithDomain(key,val){
  var str = escape(key) + "=" + escape(val);
  str += "; domain=.mydomain.com"; // 頭に "." を付けたドメイン名を指定(ここがカギ!)
  return str;
}
</script>
</head>
<body>
<input type="button" value="test1-set" onclick="setMyCookie('username1','password1')">
<input type="button" value="test2-set" onclick="setMyCookie('username2','password2')">
</body>
</html>


次に http://www2.mydomain.com/getCookie.html で http://www1.mydomain.com/setCookie.html でセットしたクッキーを取り出します。サブドメインの異なる2つのサーバー間で同じクッキーをやりとりしている点に注目:
(getCookie.html)

<html>
<head>
<script type="text/javascript">
function getMyCookieWithDomain() {
  var s="";var t="";
  cookies = document.cookie.split("; ");  // www1 でセットしたクッキーを www2 で取り出している
  for (i = 0; i < cookies.length; i++) {
    str = cookies[i].split("=");
    if (unescape(str[0]) == "username") s="id = "+unescape(str[1]);
    else if (unescape(str[0]) == "password") t="pass= "+unescape(str[1]);
  }
  return s+"\n"+t;
}
</script>
</head>
<body>
<input type="button" value="TEST" onclick="alert(getMyCookieWithDomain())">
</body>

上記のように、クッキーを設定する際(getCookie.html)のドメイン指定時に、頭に "." を付けたドメイン("mydomain.com" ではなく ".mydomain.com")を指定して保存します。これがカギのようです。

これで、www1.mydomain.com でクッキーを設定した後に www2.mydomain.com でそのクッキーを取り出して使う、という処理が実現できます。異なるアプリケーションサーバーで同じ情報を覚えさせて使う場合に便利な方法ですよね。

なお、上の例ではユーザー名とパスワードをクッキーに保存して取り出す、というサンプルを紹介していますが、実際のアプリではパスワードをクッキーに入れるのは危険なので、あまりオススメしません(苦笑)。


IoT アプリケーションを作っていると、ローカルで MQTT 環境を使いたくなる時があります。

Mosquitto は、そんなオープンソースな MQTT のコントリビューションです。Windows や Mac 向けのバイナリも用意されているようですが、自分は Linux(ラズパイやCentOS) で使えると便利なので Linux 用の環境構築手順を紹介します。

まず、ラズパイだと話が早く、このコマンドを入力するだけで MQTT のブローカーが導入できてしまいます:
$ sudo apt-get install mosquitto

MQTT のパブリッシャーやサブスクライバーといったクライアントコマンドを使う場合は、更にコマンドを実行します:
$ sudo apt-get install mosquitto-clients


ブローカーは mosquitto というサービスとして導入されるので、service コマンドで他のサービス同様に扱うことができるようになります:
$ sudo service mosquitto status


一方、CentOS の場合は少し準備が必要です。インストールそのものは yum を使うので、まずは root でログインしてリポジトリを導入します:
# cd /etc/yum.repos.d
# wget http://download.opensuse.org/repositories/home:/oojah:/mqtt/CentOS_CentOS-6/home:oojah:mqtt.repo

リポジトリが準備できたら yum でインストールします。これはブローカーのインストールコマンドです:
# yum install mosquitto

パブリッシャーやサブスクライバーなどのクライアントコマンドをインストールする場合はこちらのコマンドを(も)実行します:
# yum install mosquitto-clients

インストール後は、このコマンドでブローカーのサービスが起動します。ちなみに TCP と UDP の 1883 番ポートを空けておく必要があります:
# /usr/sbin/mosquitto

まあどちらも簡単でした。これでローカル環境に MQTT ブローカーの構築ができてしまいました。



最後に動作確認をしてみます(動作確認には MTQQ クライアントコマンドを導入した環境が必要です。MQTT ブローカーと同じマシンでも異なるマシンでも構いません)。

MQTT ブローカーが稼働している状況で、MQTT クライアントコマンドの使えるターミナル(やコマンドプロンプト)を2つ用意します。1つがパブリッシャー(サーバー)、もう1つがサブスクライバー(クライアント)となります。

まず1つのターミナル内で MQTT サブスクライバー(クライアント)を起動します。その際に -h オプションで MQTT ブローカーが稼働しているホストを、-t オプションでトピック文字列をそれぞれ指定します。起動後はプロンプトが戻らず、(Ctrl+C で終了するまで)待ち続けます:
# mosquitto_sub -h 192.168.0.XXX -t "top/ic001" -v

次にもう1つのターミナル内で MQTT パブリッシャー(サーバー)を起動します。起動時に -h オプションでサブスクライバーと同じ MQTT ブローカーホストを、-t オプションでサブスクライバーと同じトピック文字列をそれぞれ指定します。また -m オプションでメッセージ(この例では "Hello, World.")を指定します:
# mosquitto_pub -h 192.168.0.XXX -t "top/ic0t01" -m "Hello, World."
# mosquitto_pub -h 192.168.0.XXX -t "top/ic001" -m "Hello, World."

成功するとパブリッシャー側はすぐにコマンドプロンプトが戻って終了しますが、サブスクライバー側には送信されたメッセージが表示され、そのまま次のコマンドを待ち続けるはずです:
# mosquitto_sub -h 192.168.0.XXX -t "top/ic001" -v
top/ic001 Hello, World. ←パブリッシャーから送られたこれがサブスクライバー側に表示される

動作確認もできました。3台(ブローカー、サブスクライバー、パブリッシャー)の役割分担も分かりましたね。



(参考にしたサイト)
http://flyerback.blogspot.jp/2014/04/mqtt-broker-mosquitto-amaazon.html


このページのトップヘ