投稿日

SORACOM AirとBeamと時々NAT越え

はじめに

ソラコムCTOの安川です。この書き出しでブログを書くのは実は今回が初めてで、ちょっと緊張しています。

9月30日に皆様にお披露目したIoT Platform SORACOMはたくさんの皆様に注目を頂き、これまで構想から開発、リリースまで力を注いできたチームの一人としてこれ以上ない喜びを感じています。核であるモバイル通信サービス、SORACOM Airの加入者数も、おかげさまで今月は純増数過去最高を記録しております(今月が初月なので当たり前 笑)。

思い起こせば、初めてAWSに触れたのはEricsson Research時代にIoT/M2M, Connected Home/Car/Thingの研究プロジェクトに携わっていた時でした。当時、たくさんのモノが繋がるシステムの絵を描きながら、その中心にあるクラウドの可能性に魅せられ、もっと深く追求したい、その力を使いこなせるようになって世の中に貢献したい、そんな思いでAWSソリューションアーキテクトとなった経緯です。玉川と出会ったのもその時で、運命的な出会いであったなと思います。

SORACOMの構想に至るもっと前段階で、小さなきっかけとなるアイデアが生まれたのは玉川とたまたま二人で飲んでいた時だったというエピソードは以前、大谷イビサさんのインタビュー記事に書いて頂きました。「ソフトウェアで実現できることはクラウドでより良く実現できる」という実感は何年かのAWSでの経験を経てどんどん強くなっていき、Ericsson時代に見ていたIoTのバックエンドやモバイルネットワークのコア機能だってクラウドで実現できるはずと自然に考えていました。そんな考えは口にせずにいたら、あるいは他の場面でつぶやくだけだったら本来消え行くだけの考えだっただろうと思いますが、その晩目の前に玉川憲がいてその話をしたこと、その後共同創業者の船渡を始め、かけがえのない仲間たちが一人、また一人と集うに連れ、小さな灯火が確かな光になっていきました。

「アイデアとか何かが出来るってこと自体は大事じゃない。大事なのは如何にうまく実行するかだ。」これはシアトルのDynamoDB開発チームにいた時の尊敬する元上司であり、大切な友人でもあるAWSのKhawaja Shamsが、彼の運転でUS101をドライブしながら相談した時に伝えてくれた言葉です。それを思い返しながら、最初のきっかけから今に至る経緯を考えてみると、このチームで実行したからこそ今回のリリースが実現出来たこと、誰か一人欠けても今のSORACOMには辿りつけなかったことを実感して目頭が熱くなりました。「フルスケールエンジニア」というキーワードの名付け親であり、SORACOMのAPIをはじめあらゆる要所の守護神であるOguの言葉を借りると、本当に愛おしいチームです。

また、いろんな予定を変更してソラコムの起業という選択をするにあたって、相談したら真っ先に応援して背中を押してくれた妻にも感謝してます。実は二子玉川オフィスの内装を担当したのはうちの妻だという隠れ話もあります。リレーブログの先陣を切ってくださったはてなの田中さんのブログで「コアの開発はすごく難しいわけではなかったと言ってた」という部分を読んで、「さんざんフィールド試験である場所を行ったり来たりしてたくせに(・∀・)」とツッコミを受けたりもしたので、この場で素直に感謝の気持ちを表します。いつもありがとう!

と、このまま思いを語り続けると、夜のテンションの高さもあり、いつまでも振り返りと感謝感激の言葉ばかりで終わらなくなってしまいそうな気がするので、技術ブログとして読んでくださっている皆様が去ってしまう前に次の話題に移りたいと思います。

デバイスからクラウドはわかった。でもその逆は?

おかげさまでSORACOM Airだけでなく、SORACOM Beamはたくさんのお客様にご好評頂いていて、デバイス側からクラウドあるいはインターネット上のサーバへのデータの転送を、デバイス側の負担を軽減しながらセキュアrにより柔軟に実現できるということを実感していただいているように感じています。M2M/IoTの研究プロジェクトをしながらデバイスのマネージメントをどうするかで悩んだ経験を思い返すと、その部分をプラットフォームに任せて、本来実現したかったユースケースに集中して頂ければ何よりと思う次第です。

一方、最近何度か続けて、お客様から次のような質問を頂くようにもなりました。

「デバイスからクラウドにBeamでセキュアにデータ転送できるのはわかったよ。でもクラウドからデバイスにメッセージを送りたい時とか、デバイスからデバイスにデータ送りたいときはどうするの?」

これはごもっともなご質問だと思います。

実はSORACOM Air及びBeamは9/30の発表前からプライベートβという形で一部のお客様やパートナー様に提供をさせて頂き、フィードバックを頂いていました。その時も、上記のようなご質問もなかったわけではないのですが、どちらかというとデバイス側でポートを開けて待ってしまうとその部分のセキュリティ対策が必要になってしまうという懸念から、信頼するサーバにデバイスからコネクションを張る形や、MQTTでSubscribeさせてサーバからメッセージをPublishする形式の方が好まれる傾向が強いようでしたので、直接アクセスをさせるような手段は用意しませんでした。むしろファームウェアの更新や、不定期なコマンドの実行といったユースケースであれば、デバイスを一定期間眠らせて、起きた時に取りに行くような非同期なシステムの構築の方が電力消費の観点でも望ましいというお客様の声もありました。

それを受けると、先日のre:Inventで発表されたAWS IoTは正にこういったユースケースにうってつけのサービスかなと思います。MQTTでデバイスからメッセージを送りながら、その内容に応じてアクションを実行したり、デバイスがオフラインの時にもクラウド側からのメッセージを一旦受け取って保留しておいてくれるThing Shadowという仕組みがあったり、至れり尽くせりなサービス内容の発表に私自身もしびれておりました。

先日のクラスメソッドさん主催のre:Invent振り返り勉強会「re:Growth」でもAWS IoTは注目の的で、ソラコムも登壇枠を頂いたのでSORACOM BeamからAWS IoTにメッセージを投げて、Amazon SNSで通知メッセージを飛ばすデモをしまして、好評を頂きました。

Beam+AWSIoT

デモの構成(re:Inventに行けなかった私はIoT Buttonがもらえず、持って帰ってきた人のを借りて押そうにも技適認証通ってないので押せないということで仕方なく私物のXperiaに擬似IoT Buttonを作ってデモったのでした)

で、今日はその内容について書こうかな、とも思ったのですが、私の後のバトンの受け取り手がMr. AWS IoTとも言うべきAWSの榎並さんなのでここはあえてスルーパスをして、別の話題にしてみます。

クラウド経由のメッセージングはいいけど、そうはいってもクラウドからデバイスの間にソケット張りたいときはあるでしょ

前述のとおり、メッセージやコマンドのやり取りであれば、AWS IoTを始め、AWSの各種サービスを使ったり、どこかにメッセージの受け渡しを行うサーバを用意すれば済むかと思います。でも、やっぱりソケット張りたいときはありますよね?例えば:

  1. メンテやデバッグなどの目的でリモートのデバイスのシェルにアクセスしたい
  2. カメラやマイクなどからのストリームデータの受信のためには中継点を通さずに1対1接続で行いたい

先述の通り、セキュリティ上の懸念を意識して、SORACOM Airは少なくとも現状、任意のソースからデバイスに向けてコネクションを張りに行けないようになっているため、単純には行きません。しかし、予め上記のような用途が予想される場合にはデバイス側に仕込みをしておくことで解決できる可能性があります。

1つお手軽な手段としてはデバイス側から信頼できるサーバに対してTCPソケットを張って、ポートフォワーディングでデバイスの特定のポートにマッピングしておく方法があります。

例えば、デバイスからSSHでリモートサーバのポート8022をデバイス側のSSHサーバ22にマッピングする場合、

$ ssh -R :8022:localhost:22 <remote server>

などとしておけば、:8022か、同サーバ上でlocalhost:8022にSSHすればデバイス側のSSHサーバにログイン出来ます。

上記はお手軽ですが、SSHの暗号化が2回かかって無駄だということであれば、デバイスからリモートサーバへの接続はPureなTCPソケットにしておくという手もあります。例:

  • サーバ側は下記のコマンドででデバイスからの接続を待ち受け、別途8022への外からのアクセスを待ち受け
$ mkfifo /tmp/fifo
$ nc -l 8022 < /tmp/fifo | nc -l <remote port> > /tmp/fifo
  • デバイス側はサーバのにソケットを張り、データが来たらローカルの22番に転送
$ mkfifo /tmp/fifo
$ nc <remote server> <remote port> < /tmp/fifo | nc -l 22 > /tmp/fifo

上記のコマンドを実行すると、ポートフォワーディング用のTCPソケットが張られて、SSHの場合と同様、サーバ側の指定ポートへの通信(8022)がデバイスの22番ポートにマッピングされます。前述の例と違って、SSHの暗号化処理が2重に実施されることがないので少し無駄が減ります。

また、サーバ側のプログラムを少し作り込めるようであれば、SORACOM Beamを活用して、繋いでくるクライアントの認証することも出来ます(SORACOM BeamのオプションでSIMのIDのであるIMSIを付加できる。付加されたIMSIが改ざんされてないことを検証するためのデジタル署名も付与できるので、安心してクライアントを認証可)。また、SORACOM Beamからサーバまでの間の区間だけTCP over SSL/TLSで保護するなんてことも出来ます(下図に設定例)。

Beam Config Example

その場合には予めSORACOM Beamの設定を投入した上で、デバイス側のコマンドを:

$ nc beam.soracom.io 8023 < /tmp/fifo | nc -l 22 > /tmp/fifo

とすれば良いでしょう。

デバイスからデバイスへのPeer to Peer通信はどうするの?

これに関しては別段SORACOM Air特有のことはなく、一般的なNAT超えをしていただくのが良いのではないかと思います。

例えばVoIPやWebRTCなどのUDPを使ったリアルタイム通信の場合には、多くの場合STUNやTURNを使ったNAT越えが組み込まれているソフトウェアが使われるかと思いますので、STUN/TURNサーバを設定しておけば、SORACOM Airと別のNATの内側のデバイスとの通信、SORACOM Air同士の通信でも問題なく実現出来ますし、実際にそういった形でお使いのお客様もいらっしゃいます。

また、TCPの場合にはSTUNは使えないという話もよく聞きますが、実は対応したサーバと対応したクライアントであればSTUNを使ったTCPのNAT越えというのものも不可能ではありません。

実際、STUNTMAN というソフトウェアをインストールして、SORACOM Airで繋がったデバイス同士で試してみました。ここではデバイス1, 2として、それぞれで下記のようなコマンドを実行すると、実行結果としてローカルのIPアドレスとポートの組がどのグローバルIPアドレスとポートにマップされたかが帰ってきます。

$ stunclient --protocol tcp <tcpに対応したstunサーバ>
Binding test: success
Local address: 10.224.128.252:33933
Mapped address: 52.xx.yy.zz:33933

後はこの情報を何らかの方法で交換した上で、Sourceポートを先ほどSTUNでマッピングを調べた際に使ったローカルポート(上の実行結果を得たホストの例だと33933)に指定してソケットを張ります。

$ nc -p 33933 <PeerのIPアドレス> <Peerのポート>

こうすると、お互いNATの内側にいても、すなわちSORACOM Airで繋がったデバイスであってもTCPソケットを張ることが出来て、上記の例の用にポートフォワーディングを設定していれば、フォワード先のポートに対して通信が可能になります。

TCP STUN

ポイントは、各PeerでSTUNを使ってポートマッピングを確認した上で、お互いのポートをそれぞれSource/Destに指定してTCPソケットを張りに行っている点です。これによって、間に存在するNATテーブルに対応するエントリが作られて、逆方向のパケットが通るようになります(UDPの場合と同様)。TCPではSourceポートに動的に割り当てられるEphemeral Portが用いられるのが一般的なので馴染みがない形に見えますが、要はUDPのNAT越えと同じようなことをしているというわけです。ncなど、Sourceポートが指定できるプログラムだとこういったことも可能ですね。

当然、実用する上ではポートと宛先のIPアドレスを交換するための何らかのシグナリングが必要になってしまいますが、その辺をどうにかすればPeer to Peer
の通信も出来るということで、応用できる場面がありましたら思い出して頂ければと思います。

おわりに

今回は最近何度か同様のお問い合わせを頂いた経緯もあり、中継サーバとポートフォワーディングを活用したクラウドからデバイスへの通信、NAT越えによるデバイスからデバイスへの通信を行う例をご紹介しました。

本当はもう一つ、SORACOM Beamがまだ名前もついておらず、私の頭の中の妄想から具体的にどういうものかをデモするのに作った仮想のサービス/アプリがあって、それが正にここで話したようにTCPで繋がったデバイスをIMSIで認証して、外からは認証付きのWebソケットで繋げばデバイスと直接通信出来るというシステムだったので、前半の終わりでご紹介した内容に近いことから、ここで例として掲載しようかと思ったのですが、ちょっとすでに長くなっちゃったので、またの機会にするか、ご興味ある方に別途お見せ出来ればと思います。

なお、今回一般的にNAT超えやポートフォワーディングなどを活用したデバイスへのアクセスをご紹介しましたが、SORACOM Airで繋がったデバイスにやはり直接アクセスする手段は欲しいよという声がありましたら、それはそれで実現したいユースケース含めてご相談頂ければと思います。
お客様の声を反映して進化していくという点もIoT Platform SORACOMの特徴の1つですので、そういった声も是非お聞かせ下さい!