VPS + Flask で、おうちの状況をどこでも確認


  • 今年は Advent Calendar に 2 つ参加することができました。

Prologue

  • IoT 家電が普及してきたことで、家の各種状態を
    外出先から見るということの需要が増えてきました。

  • でも、家庭内 LAN の機器に WAN から接続するためには
    NAT 越えなどのややこしい話をクリアする必要があり、
    一歩間違えば家庭内 LAN を脆弱にしてしまうリスクもあるため、
    安易にオススメできません。

  • そこで…

VPS を使って家庭内 LAN と公開サーバーを切り離そう

  • 今は VPS のプロバイダーや契約プランが増え、ワンコインから
    契約できるため、気軽に利用できるようになりました。

  • VPS を利用して、家庭内 LAN の機器を WAN に公開することなく、
    家庭内の状況を監視するようにします。

仕組み概念図

  • 家庭内 LAN 側からのアクションは upload だけなので、
    家庭内の機器がセキュリティリスクに曝されることはありません。

VPS を準備する

  • まだ VPS を契約していない方々

    • 契約してください!
    • ググったら各社の特徴比較の記事がいっぱいあります。
  • 準備の内容は、ここでは省略します。

    • ご契約の会社が公開している手順をご覧ください。
    • どの手順でも、終わった段階で作業 PC から VPS に
      ssh で接続できるようになっているはずです。

家庭の状況を収集し、upload する仕組みを作る。

  • ここでは簡単な事例として、RasPi で定期的に vmstat を実行し、
    その直近 10 回を表示することにします。
    • ここでは、こちらで準備した Pi4 を使います。

秘密鍵と公開鍵を作る

  • まず RasPi から password 認証無しに VPS に
    ssh で login できるように秘密鍵と公開鍵を準備します。
pi $ ssh-keygen -t ed25519
  • デフォルトのままポチポチしたらこうなっていると思います。
pi $ ls ~/.ssh/
id_ed25519  id_ed25519.pub
  • VPS をセットアップした作業 PC から rsync で
    id_ed25519.pub を引っ張ってきて、VPS に上げます。
PC $ rsync -av [email protected]:~/.ssh/id_ed25519.pub ./
PC $ rsync -av id_ed25519.pub [VPS USER]@[VPS IP]:~/
PC $ ssh [VPS USER]@[VPS IP]
VPS $ cat ~/id_ed25519.pub >> ~/.ssh/authorized_keys
  • ~/.ssh/config を書きます。
    • ホスト名は、ここでは仮に flask-vps とします。
Host    flask-vps
    HostName    aaa.bbb.ccc.ddd
    Port        xxxxx
    User        username
    IdentityFile    ~/.ssh/id_ed25519
  • RasPi から VPS に login できることを確認します。
pi $ ssh flask-vps
VPS $ 

make_report.sh

  • 次に vmstat の結果と、実行した日時を txt に出力する script を作ります。
#!/bin/bash

date
echo
vmstat -S M 2 5
echo
  • ここの内容をもっと工夫することで、様々な情報を収集することができます。

submit.sh

  • 更に make_report.sh で log_report.txt を作成し、定期的に
    最後の方を切り出して VPS に upload する shellscript を作ります。

    • ファイル名のネーミングが今イチですがご容赦ください。
  • 無限 loop で service として動かすため、ファイルを別にしました。

    • make_report を function にして 1 ファイルにすることもできます。
  • upload する VPS のディレクトリは、ここでは
    ~/workspace/home_report/ とします。

#!/bin/bash

LOG='log_report.txt'
PERIODIC_MINUTES='10'
VPS_SVR="flask-vps:~/workspace/home_report/static"

while :
do
    ./make_report.sh >> $LOG
    cat $LOG | tail -n 100 > recent_${LOG}
    rsync -av recent_${LOG} $VPS_SVR/
#   exit 0
    sleep $(($PERIODIC_MINUTES * 60))
done
  • 実行した後、VPS 側できちんと upload できていることを確認します。

  • できていたら、RasPi で submit.sh を service 実行します。

$ nohup ./submit.sh &
$ exit
  • これで、RasPi が稼働中で、ネットワークに接続されている間は
    定期的に recent_log_report.txt が VPS に upload されます。
    • シャットダウンや再起動を行うと停止するので、
      その際はまた nohup ./submit.sh & してください。

upload された家庭の状況を Flask で公開する

  • ここまでの仕組みで ~/workspace/home_report/static/
    の中に定期的にデータが上がってくるので、あとはこのデータを
    Web ページのコンテンツとして表示するだけです。

home_report.py

  • あまり書くことないです。
"""
advent calendar 2019 flask.
"""

from __future__ import absolute_import, division, print_function, unicode_literals
import sys
from os import path
from flask import Flask, render_template

app = Flask( __name__)

@app.route( '/')
def entrance() :

    return  "{}".format( __name__)

@app.route( '/home-report')
def default_report() :

    with open( 'static/recent_log_report.txt', 'r') as file :
        report  = file.readlines()

    return  render_template( 'report.html', report = report)

def main( argv) :

    host_ip     = argv[1]
    host_port   = int( argv[2])

    app.run(    host    = host_ip,
                port    = host_port )

if __name__ == "__main__" :

    if len( sys.argv) < 3 :
        print( "USAGE: {:s} [ip] [port]".format( sys.argv[0]))
        sys.exit( 1)

    main( sys.argv)

templates/report.html

  • 最低限です。
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>Home Report</title>
        <style>
            div.report {
                background-color    : #EEE;
                font-family         : monospace;
                max-width           : 800px;
                padding             : 20px;
                margin              : 0 auto 0 40px;
            }
        </style>
    </head>
    <body>
        <h1>Home Report</h1>

        <div class="report">
            <!-- {{report}} -->
            {% for line in report %}
                {{line}}<br>
            {% endfor %}
        </div>
    </body>
</html>

VPS 側で Flask を実行する

  • ここまでで、VPS の作業ディレクトリの中はこんな感じです。
~/workspace/home_report/
|-- home_report.py
|-- static/
|  `-- recent_log_report.txt
`-- templates/
   `-- report.html
  • service として実行します。
VPS $ export IP=`networkctl status | grep -i address | awk '{print $2}'`
VPS $ export PORT='xxxxx'
VPS $ nohup python3 home_report.py $IP $PORT &
VPS $ exit

完成

  • ブラウザーで VPS の http://IP:PORT/home-report にアクセスすると、
    static/recent_log_report.txt の内容が表示されました。

    • 見た目はショボいですが、
      直近の家庭の状況が見られるサイトができました。

Epilogue

  • 今回は RasPi の情報だけでしたが、
    RasPi から収集可能な家庭内のテキストデータや画像なら
    何でも upload することができるため、汎用性は高いです。

  • VPS の入門ネタとしても、ぜひご活用ください


EOF