AWS IoT セキュアトンネリングを使ってローカルのRaspberry Piにリモートから接続してみた

2020.02.11

まいど、大阪の市田です。
今回は表題のとおりですが、AWS IoT セキュアトンネリングを使ってローカルにあるRaspberry Piにリモートから接続してみたいと思います。

構成

今回の構成のポイントは以下です。

  • Raspberry Piは外部から直接アクセスできないローカル環境にあります。
  • Raspberry Pi上でWebサーバを稼働させます。
  • このWebサーバはlocalhostに対してのみListenするようにします
  • このWebサーバで稼働しているサンプルページに、EC2からブラウザで接続します。

00-diagram

この構成に利用した各種リソースの詳細は下記になります。

  • EC2
    • OS:Amazon Linux 2
    • ブラウザ:Chromium
    • VNC:tigervnc-server
  • Raspberry Pi
    • 筐体:Raspberry Pi 3B +
    • OS:Raspbian Stretch
      • 最後のリリースである2019-04-08を使いました

色々と組み合わせを試してみましたが、Raspbian Busterでは途中でコンパイルエラーとなり、インストールできなかった為、Raspbian Stretchを利用しています。
そのため、筐体も「Raspberry Pi 3B +」で試しています。

環境構築

以前の記事の通り、AWS IoT セキュアトンネリングには、接続する双方でlocalproxyアプリケーションを使います。
まずは、このlocalproxyのインストールに必要な環境を作っていきたいと思います。

必要な要件等は以前の記事を御覧ください。

Github上のREADMEにある通り、依存関係のあるビルドディレクトリdependenciesを作成して、そこで作業を行っていきます。

mkdir dependencies
cd dependencies

注意事項

必要に応じてRaspberry Piでsudo apt updateを実行してリポジトリを更新おくべきですが、apt upgradeも行うと以後の作業時にコンパイルが失敗します。

これは環境に依存する可能性がありますが、下記よりダウンロードしたイメージではProtobufのインストールが失敗してしまいました。

Index of /pub/raspberrypi/raspbian/images

調査しましたがcmakeのパッケージ、またはバージョンに依存しているようで、関連するissueも起票されているようです。(確定情報ではありませんのでご注意ください)

Protobuf library doesn't build for Raspberry Pi 3B · Issue #2 · aws-samples/aws-iot-securetunneling-localproxy

似たような原因と思われるissueもあり、色々と試してみましたが現時点(2020年2月11日時点)では、まだ解決できていません。また、Raspbian Busterでも同様で、唯一「Raspberry Pi 3B+ と Raspbian Stretch」の組み合わせ でlocalproxyがインストールできました。

もしRaspberry Piで試してみたい方がいらっしゃれば、まずはStretchにてapt upgradeしない状態で試してみて頂ければと思います。

Zlibダウンロードとインストール

zlibはaptでインストールできるものを利用します。

sudo apt install zlibc

Boost依存関係のダウンロードとインストール

Githubの手順にある通りで問題なくインストールできました。
ただし、「Raspberry Pi 3B +」である為、./b2 installの完了に2時間ほどかかりました。

wget https://dl.bintray.com/boostorg/release/1.69.0/source/boost_1_69_0.tar.gz -O /tmp/boost.tar.gz
tar xzvf /tmp/boost.tar.gz
cd boost_1_69_0
./bootstrap.sh
sudo ./b2 install

Protobuf依存関係のダウンロードとインストール

ここも特に詰まることなくインストールできました。しかし先程と同様にmakeが完了するまでに、また2時間ほどかかりました。

cd ~/dependencies
wget https://github.com/protocolbuffers/protobuf/releases/download/v3.6.1/protobuf-all-3.6.1.tar.gz -O /tmp/protobuf-all-3.6.1.tar.gz
tar xzvf /tmp/protobuf-all-3.6.1.tar.gz
cd protobuf-3.6.1/
mkdir build
cd build
cmake ../cmake
make
sudo make install

Catch2テストフレームワークのダウンロードとインストール

ここも時間が2時間ほどかかりますが、問題なく完了できました。

cd ~/dependencies
git clone https://github.com/catchorg/Catch2.git
cd Catch2
mkdir build
cd build
cmake ../
make
sudo make install

ローカルプロキシのダウンロードとビルド

順調に進んでいましたが、今回の作業でcmakeが失敗し、次のようなエラーメッセージが出ていました。

CMake Error at /usr/share/cmake-3.7/Modules/FindPackageHandleStandardArgs.cmake:138 (message):
  Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the
  system variable OPENSSL_ROOT_DIR (missing: OPENSSL_LIBRARIES
  OPENSSL_INCLUDE_DIR)

ということで、libssl-devをインストールします。

sudo apt install libssl-dev

再度、localproxyのインストールを実行して無事に完了しました。

cd ~/dependencies
git clone https://github.com/aws-samples/aws-iot-securetunneling-localproxy
cd aws-iot-securetunneling-localproxy
mkdir build
cd build
cmake ../
make

確認

事前準備

localproxyアプリケーションがインストールできたので、動作確認を行います。

早速、AWSIoTPythonSDKをRaspberry Piにインストールしたいところですが、以前紹介した「IoTエージェント」で使っているsubprocess.runは「Python 3.5」以上である必要があります。
Raspbian StretchのデフォルトはVer2系のPythonであるため、python3をインストールしておきます。
(仮想環境で用意することも考えましたが、動作確認を優先するため、python3をそのままインストールしました)

sudo apt install python3

次にSDKをインストールします。

sudo pip3 install AWSIoTPythonSDK

動作確認用のWebサーバを準備

前回はSSHアクセスを試したので、今回はlocalhostで起動するWebサイトにアクセスできるかどうか試してみたいと思います。

そのため、Raspberry Pi上にWebサーバを立てます。

sudo apt install apache2

デフォルトだと全公開でListenするので、/etc/apache2/ports.confを編集します。

Listen 80

↓

Listen localhost:80

netstatで確認した状態が下記です。localhostのみListenしていることが分かります。

tcp  0  0 127.0.0.1:80    0.0.0.0:*    LISTEN   4350/apache2

トンネルを開く

Raspberry Pi側の下ごしらえは終わったので、AWS CLIやマネージメントコンソールからトンネルを開きます。
(トンネルを新たに開くと6ドルの課金が発生するので乱発には注意しましょう)

aws iotsecuretunneling open-tunnel \
--destination-config thingName=TunnelTestDevice,services=http

IoTエージェント起動

次に、ラズパイ上にIoTエージェントを用意して起動します。エージェントスクリプトは以前EC2で利用したものと同じものになります。
ただし今回はlocalhostで稼働するWebサイトにアクセスしたいので、スクリプトを一部修正します。(-dオプションの指定箇所)

subprocess.run([
    "/home/ec2-user/aws-iot-securetunneling-localproxy/build/bin/localproxy",
    "-t", json_message['clientAccessToken'],
    "-r", "ap-northeast-1",
    "-d", "localhost:22"
])

↓

subprocess.run([
    "/home/ec2-user/aws-iot-securetunneling-localproxy/build/bin/localproxy",
    "-t", json_message['clientAccessToken'],
    "-r", "ap-northeast-1",
    "-d", "localhost:80"
])

また、このRaspberry Piを「IoTレジストリ」に登録して各種証明書をラズパイ上に保存しておきます。
(モノの登録や証明書のダウンロードなどの作業内容は割愛致します)

準備ができたらエージェントを起動させます。

python3 IotAgent.py \
--endpoint xxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com \
--rootCA ../cert/rootCA.pem  \
--cert ../cert/xxxxxxxxxx-certificate.pem.crt  \
--key ../cert/xxxxxxxxxx-private.pem.key

アクセストークンをPublishしてlocalproxy起動

トンネルを開いたときに取得できるアクセストークン(destinationAccessToken)をマネジメントコンソールからPublishして、IoTエージェントからlocalproxyを起動させましょう。

以前と同じIoTエージェントをそのまま使っているので、以前と同じトピック名「$aws/things/TunnelTestDevice/tunneling/notify」にPublishします。

{
  "clientAccessToken": "destinationAccessTokenの中身"
}

下記のようなメッセージがRaspberry Pi上で確認できればOKです。

Successfully established websocket connection with proxy server: wss://data.tunneling.iot.ap-northeast-1.amazonaws.com:443

接続元EC2の準備

接続元に使うEC2は前回と同じインスタンスを利用します。ただし、今回はグラフィカルに確認してみたかったのでAmazon Linux 2にGUI環境をインストールしました。

Amazon Linux 2へのGUI環境の構築は下記を参考にして頂ければと思います。

次に、このEC2にブラウザ(chromium)をインストールします。

sudo amazon-linux-extras install epel
sudo yum install chromium

接続元ローカルプロキシの起動

これでEC2側の環境整理が完了しましたので、接続元となるlocalproxyを起動させます。
今回は8080ポートを指定して起動させてみました。

$ ./localproxy -r ap-northeast-1 -s 8080 -t <sourceAccessTokenの中身>

ブラウザで動作確認

これで全ての準備が完了しました!
それでは、EC2からRaspberry Pi上のWebページにアクセスしてみましょう!

まずは、手元のPCからEC2にVNCで接続します。私はMacを利用しているので、MacのFinderを使って、EC2インスタンスにVNC接続しました。接続できたらブラウザを起動します。

01-ec2-vnc-browser

localhost:8080をブラウザでアクセスしてみると、Raspberry Pi上のWebページを表示させることができました!

30-localhost-web

最後に

少々導入が手間ですが、メンテナンス用途などに限れば、AWSのサービスだけでセキュアにデバイスにアクセスできるのは便利だと思います。

参考ページ

余談

EC2では各作業がサクサクと進んだのですが、Raspberry Piの場合だと、それぞれのインストールに非常に時間がかかりました。また、最新のRaspbian(Buster)で動くと思っていたのですが、色々と試してみてもインストールできず何度も試してみたので更に時間がかかってしまいました。

そのため、夜にスタートさせたまま寝て翌朝に作業再開ということを何日か繰り返しました…

Busterで接続できない点については、今後も調査を継続していきたいと思います。