siren.

pythonで外形監視とSSL証明書の有効期限を監視を監視してみた

Python
サービスを使うのが確実だけど、個人用途なのでサクッと作って Line で通知してみました。
必要なのは python と env 読み込み用のパッケージ。

  1. LINE Notify でトークン払い出し
  • LINE Notifyにアクセス
  • 登録サービス管理からサービスを登録するとトークンが払い出されるのでメモする
  1. 必要パッケージのインストール
トークンとか設定値は外だしするので下記パッケージをインストール
pip install python-dotenv
  1. .env ファイルの設定
    MONITOR_SITE は複数サイト監視する場合は,で区切る
LINE_TOKEN=YOUR_LINE_NOTIFY_TOKEN
LINE_NOTIFY_API=https://notify-api.line.me/api/notify
MONITOR_SITES=hoge1.jp,hoge2.net,hoge3.com
SSL_EXPIRY_DAYS=30
  1. 実行と登録
    試しに、サイトを停止した上でスクリプトを実行し、Line へ通知がくれば OK。
    ログはスクリプトと同じディレクトリに monitoring.log に出力されます。
python script.py

cron で実行登録。実行間隔はお好みで(下記は5分ごと)

*/5 * * * * /usr/bin/python3 /path/to/your/script.py

python コード

from datetime import datetime
import os
from dotenv import load_dotenv
import pytz
import ssl
import socket
import requests
import logging

# .env ファイルから環境変数を読み込む
load_dotenv()

# 環境変数の取得
LINE_TOKEN = os.getenv("LINE_TOKEN")
LINE_NOTIFY_API = os.getenv("LINE_NOTIFY_API")
MONITOR_SITES = os.getenv("MONITOR_SITES").split(',')
SSL_EXPIRY_DAYS = int(os.getenv("SSL_EXPIRY_DAYS"))

# ロギング設定
logging.basicConfig(level=logging.INFO, filename='monitoring.log')

# LINEにメッセージを送る関数
def send_line_message(message):
    headers = {"Authorization": f"Bearer {LINE_TOKEN}"}
    payload = {"message": message}
    requests.post(LINE_NOTIFY_API, headers=headers, data=payload)

# SSL証明書の残り日数を取得
def get_remaining_days(hostname):
    context = ssl.create_default_context()
    conn = context.wrap_socket(socket.socket(socket.AF_INET),
                               server_hostname=hostname)
    conn.connect((hostname, 443))
    cert = conn.getpeercert()
    not_after = datetime.strptime(cert['notAfter'], r"%b %d %H:%M:%S %Y %Z").replace(tzinfo=pytz.UTC)
    tz = pytz.timezone('Asia/Tokyo')
    now_aware = datetime.now().astimezone(tz)
    remaining = (not_after.astimezone(tz) - now_aware).days
    conn.close()
    return remaining

# サイトをチェックする関数
def check_sites():
    for site in MONITOR_SITES:
        try:
            # ステータスコードのチェック
            response = requests.get(f"https://{site}")
            if response.status_code != 200:
                send_line_message(f"Status code error: {site} returned {response.status_code}")
                logging.error(f"{datetime.now()} - Status code error: {site} returned {response.status_code}")

            # SSL証明書の有効期限をチェック
            remaining = get_remaining_days(site)
            if remaining <= SSL_EXPIRY_DAYS:
                send_line_message(f"SSL certificate alert: {site} will expire in {remaining} days")
                logging.warning(f"{datetime.now()} - SSL certificate alert: {site} will expire in {remaining} days")

        except Exception as e:
            send_line_message(f"An error occurred with {site}: {str(e)}")
            logging.error(f"{datetime.now()} - An error occurred with {site}: {str(e)}")

if __name__ == "__main__":
    check_sites()