Raspberry pi サーバの起動時にIPアドレス取得

published_with_changes 更新日: event_note 公開日:

labelamazon alexa labelBlynk labelIoT labelRaspberry pi

無料で実現するため、Alexa 部屋の温度を教えて、第11版です。 自力では無理なので今回も、Google Gemini Logo に聞いて作ります。

基本方針

ラズパイの読み取るIPアドレスはルータのグローバルIPアドレスで、これは停電などでルータが再起動すると変わってしまいます。

そのため、現在はルータの起動動作に関係なく10分間隔でIPアドレスを取得するようにしています。それに呼応して、ただ単に同じアドレスを読み込みために、AWS lambdaが動きます。これが釈然としません。


そこで、これに変わる方法として、以下の2つを考えました。

仕様比較


仕様Aと仕様Bの比較

仕様Aと仕様Bの比較

項目 仕様A 仕様B
概要 ルータが再起動したら、ラズパイがIPアドレスを読み取り、DynamoDBに保管する。 ラズパイが起動するたびにIPアドレスを読み取り、DynamoDBに保管する。
実現可能性 技術的には可能だが、ルータの再起動をラズパイが直接検出するのは難しい。 比較的容易に実装可能。
検出方法
  • ルータから再起動通知を受け取る(ルータの機能依存)。
  • 定期的にpingを送信し、応答がない場合に再起動と判断。
  • インターネット接続を監視し、切断後の復旧を再起動と判断。
ラズパイの起動時にIPアドレスを取得。
注意点
  • ネットワーク負荷の増加や誤検出の可能性がある。
  • ルータの機能に依存する方法では対応が制限される。
  • ルータの再起動以外の理由でもIPアドレスが更新される。

推奨される実装:

仕様Bの「ラズパイが起動するたびに、IPアドレスを読み取る」が、実装が容易で、実用性も高いと考えます。
  • ラズベリーパイの起動時に実行されるPythonスクリプトを作成し、IPアドレスを取得してLambda関数を呼び出すようにします。
  • Lambda関数側は、現在のIPアドレス更新処理をそのまま利用します。変更しなくても問題なさそうです。

具体的な実装手順:


ラズベリーパイ側のPythonスクリプト:

  • スクリプト内で、ルータのグローバルIPアドレスを取得します。
  • 取得したIPアドレスをJSON形式でLambda関数URLにPOSTリクエストを送信します。
  • 現在のスクリプトでは、AWS認証(IAM)を使用していますが、ラズベリーパイが起動するときに限ってのみLambda関数URLにアクセスする場合、IAM認証は不要です。

Lambda関数側の変更:

  • Lambda関数は変更しません。現在のままで問題ありません。
  • IPアドレスを受け取り、DynamoDBを更新する処理は、ラズベリーパイからのリクエスト頻度に関わらず機能します。

IAM認証有無のセキュリティリスクの比較:

  • IAM認証情報(IAMのアクセスキーIDとシークレットアクセスキー)漏洩のリスクは、ラズベリーパイという特定のデバイスに依存するものであり、その影響範囲は限定的です。
    • ラズベリーパイのセキュリティは、一般的なサーバーよりも脆弱である可能性が高い。
    • IAM認証情報が漏洩した場合、AWSアカウント全体のセキュリティに影響を与える可能性がある。
  • 一方、関数URLの公開によるリスクは、インターネット全体に影響を及ぼす可能性があり、その影響範囲は広範囲です。
    • しかし、今回のシステムでは、Lambda関数はIPアドレスの更新のみを行うシンプルな機能であり、仮に不正なリクエストが送信されたとしても、システム全体に大きな影響を与える可能性は低いと考えられます。

  • 今回のシステムでは、IAM認証情報漏洩のリスクよりも、関数URLの公開によるリスクの方が低いと判断しました。よって、Lambda関数URLにIAM認証を使用しません

変更内容

1.関数URLから、AWS_IAMオプションを削除

関数URLから、AWS_IAMオプションを削除しても、関数URLは変わりません。したがって、スクリプトの LAMBDA_ENDPOINT 定義を変更することはありません。

AWS_IAMオプションを削除によって Lambda関数の内部の処理が変わるだけです。

「Lambda関数」から「設定」を選択



「AWS_IAM」を「NONE」に変更して、「保存」

2.pythonスクリプト

スクリプト変更の要件

もとのスクリプトに対する変更点を説明します。

  1. requests_aws4auth のimportとIAM認証に関連するコードを削除しました。
  2. グローバルIPアドレスを取得する方法をdigコマンドを使用するように変更しました。
  3. グローバルIPアドレスを取得する方法を変更するため、import subprocess を追加しました。
    • 以前のスクリプトでは、requests.get("https://checkip.amazonaws.com") を使用してグローバルIPアドレスを取得していました。しかし、この方法はAmazonのサービスに依存しており、将来的にサービスが停止した場合にスクリプトが動作しなくなる可能性があります。
    • より汎用性の高い方法として、dig コマンドを使用する方法に変更しました。dig コマンドは、DNSサーバーに問い合わせを行い、グローバルIPアドレスを取得するコマンドです。
    • dig コマンドを実行するために import subprocess を追加しました。subprocess モジュールは、Pythonスクリプトから外部コマンドを実行するために使用されます。
    • dig コマンドは、dnsutils パッケージに含まれています。以下のコマンドを実行して、dnsutils パッケージをインストールします。
    •               sudo apt-get update
      sudo apt-get install dnsutils
                  

  1. ネットワーク接続の遅延への対応しました。
    • スクリプトの先頭に time.sleep(30) # 30秒待機 などを追加して、スクリプトの実行を遅延させます。
    • ネットワークが確立するまで、スクリプトの実行を繰り返します。
    • is_network_available() 関数は、ping コマンドを使用して 8.8.8.8 (Google Public DNS) にpingを送信し、ネットワーク接続を確認します。

変更したスクリプト


            
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import requests
import json
import datetime
import subprocess
import time

# Lambda関数URLに変更
LAMBDA_ENDPOINT = "YOUR_LAMBDA_FUNCTION_URL"  # Lambda関数URLを入力
def get_global_ip():
    """外部サービスでグローバルIPアドレスを取得"""
    try:
        result = subprocess.run(['dig', '+short', 'myip.opendns.com', '@resolver1.opendns.com'], capture_output=True, text=True)
        return result.stdout.strip()
    except Exception as e:
        print(f"Error getting global IP: {e}")
        return None

def send_ip_to_lambda(ip):
    try:
        payload = {
            'ip': ip  # IPアドレスをpayloadに含める
        }
        headers = {'Content-Type': 'application/json'}
        response = requests.post(LAMBDA_ENDPOINT, json=payload, headers=headers)
        log_message = f"{datetime.datetime.now()} - Sent IP: {ip}, Response: {response.status_code}"
        print(log_message)
    except Exception as e:
        log_message = f"{datetime.datetime.now()} - Error sending IP to Lambda: {e}"
        print(log_message)

def is_network_available():
    """ネットワーク接続を確認する関数"""
    try:
        subprocess.check_call(['ping', '-c', '1', '8.8.8.8'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        return True
    except subprocess.CalledProcessError:
        return False

if __name__ == "__main__":
    time.sleep(30) # 30秒待機

    # ネットワーク接続が確立するまでループ
    while not is_network_available():
        print("ネットワーク接続を待機中...")
        time.sleep(5)

    ip = get_global_ip()
    if ip:
        send_ip_to_lambda(ip)
            
          

3.crontab -e の設定内容

ラズベリーパイの起動時にスクリプトを実行するように設定します。

            @reboot /home/pi/blynk-ip-reporter/report_ip.py >> /home/pi/blynk-ip-reporter/report_ip.log 2>&1
          

【 注意事項 
  • このコマンドには /usr/bin/python3 の実行コマンドがありません。
  • スクリプト report_ip.py の1行目に #!/usr/bin/env python3 というshebang(シバン)が記述されています。これは、環境変数 PATH を参照して python3 コマンドを探し、そのインタプリタでスクリプトを実行することを意味します。
  • crontab は shebang を解釈し、適切な python3 コマンドを使用してスクリプトを実行します。
  • shebang を使用しない場合は、crontab に明示的に python3 コマンドのパスを指定する必要があります。
  • shebang が記述されているため、crontab に /usr/bin/python3 コマンドのパスを明示的に指定する必要はありません。
  • 要するに、/usr/bin/python3 の実行コマンドを書いても書かなくても、いづれでも問題なく動作します。

4.logrotate の設定

現在のlogrotateの設定は、10分おきのログを前提としています。
ラズベリーパイの起動時のみログを出力するように変更するため、logrotateの設定も変更する必要があります。

logrotateno設定ファイル /etc/logrotate.d/report_ip の内容を書き換えて、ログファイルのローテーション頻度を weekly または monthly に変更します。管理者権限で実行します。

        /home/pi/blynk-ip-reporter/report_ip.log {
    weekly
    rotate 4
    compress
    missingok
    notifempty
    create 644 pi pi
}
      
  • /home/pi/blynk-ip-reporter/report_ip.log
  • → 管理対象のログファイルのパスを指定しています。
  • weekly
  • → ログのローテーションを週単位で実行する設定です。
  • rotate 4
  • → 最大4つのローテートされたログファイルを保持します(古いものから削除される)。
  • compress
  • → ローテートされたログファイルをgzipで圧縮し、ディスク容量を節約します。
  • missingok
  • → 対象のログファイルが存在しなくてもエラーを出さずにスキップします。
  • notifempty
  • → ログファイルが空の場合は、ローテーション(切り替え)を行いません。
  • create 644 pi pi
  • → ローテーション後に新しいログファイルを作成し、パーミッション「644」、所有者を「pi」、グループを「pi」に設定します。

動作検証

  • ラズベリーパイから送信されたIPアドレスが正常にDynamoDBに保存され、Alexaスキルから利用されていることが確認できます。
  • Alexaスキルは、DynamoDBからIPアドレスを取得し、Blynkサーバーにリクエストを送信して、温度と湿度データを取得しています。
  • Alexaスキルからのリクエストは正常に処理されています。
  • セッション属性からIPアドレスが取得できている場合と、DynamoDBからIPアドレスを取得している場合があります。

タイムスタンプ リクエスト元 リクエスト内容 処理結果
2025-03-05T14:29:23.593+09:00 ラズベリーパイ IPアドレス更新 (118.104.199.3) DynamoDBにIPアドレスを更新 (200)
2025-03-05T14:40:49.921+09:00 Alexaスキル セッション終了 セッション終了処理完了
2025-03-05T14:40:50.113+09:00 Alexaスキル スキル起動 DynamoDBからIPアドレス取得 (118.104.199.3)
2025-03-05T14:40:58.857+09:00 Alexaスキル 温度取得 (二階) Blynkサーバーから温度取得 (14.1230069)
2025-03-05T14:41:07.172+09:00 Alexaスキル 湿度取得 (屋根裏) Blynkサーバーから湿度取得 (51.8016739)
2025-03-05T14:41:14.584+09:00 Alexaスキル 湿度取得 (屋根裏) Blynkサーバーから湿度取得 (51.8016739)

Powered by Blogger | Designed by QooQ

keyboard_double_arrow_down

keyboard_double_arrow_down