0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【シェルスクリプト挑戦\#day4】Webサイト死活監視スクリプトの作成

Posted at

このチャレンジについて

  • 目的: セキュリティエンジニアとしての技術力向上
  • 手段: シェルスクリプト作成を通じて学習
  • 実施する事: 自動化,監視,ツール開発基礎学習など

#day4の目的:Webサイトの死活監視をスクリプト化

Webサーバーのログ解析について学習したいが、ターゲットのWEBサーバをそのためにイチからデプロイするのは、正直ちょっと面倒...。かといって、適当なWEBサーバを標的にして過負荷かけてしまった場合などを考えると課題が多い。

そこで今回は課題を絞り、「Webサイトが生きているか?」自動チェックするツール作りに一旦フォーカスします。セキュリティ評価の最初のステップである「偵察(リコネッサンス)」に該当します。(ターゲットが応答しなければ、何も始まらない)
利便性・拡張性を考慮し、リストにあるサイトの生存確認をまとめて行うツールとしました。

目次

  • 実行結果
  • 前提
  • コード全文
  • コードの詳細な解説
  • 実行方法
  • まとめ

実行結果

完成したスクリプトを実行した結果、以下のようになりました。urls.txtに記述された各サイトのHTTPステータスがターミナルに一覧表示され、多数のターゲットの状態を迅速に把握できる結果となりました。

--- サイトのステータスチェックを開始 ---
[OK]       : (200) https://example.com
[OK]       : (200) https://www.google.com
[OK]       : (200) https://jsonplaceholder.typicode.com/posts/1
[REDIRECT] : (301) http://google.com
[NOT FOUND]: (404) https://httpbin.org/status/404
[NOT FOUND]: (404) https://github.com/test-user-for-scripting-123/this-repo-does-not-exist
[ERROR]    : (000) http://this-site-is-definitely-not-exist.xyz
--- チェック完了 ---

前提

本スクリプトのテストでは、意図しない高負荷を避けるなど、倫理的な観点から安全なURLをテスト対象として選定しています。以下に、テストで使用するURLの選定理由を記載します。

  • テスト専用サイト: example.comhttpbin.orgなど、開発者によるテストが許容または想定されているWebサイト。
  • 大規模で堅牢なサイト: Googleなど、極めて大規模なインフラを持ち、学習目的の低頻度なアクセスでは影響を受けないWebサイト。

コード全文

/home/<your_user>/archive/day4/getHTTPStatus.sh

#!/bin/bash

# =============================================================
# Webサイト死活監視スクリプト (getHTTPStatus.sh)
#
# urls.txtからURLリストを読み込み、それぞれのHTTPステータスを
# 確認して結果を表示します。
# =============================================================

# --- 1. パスの設定 ---
# スクリプト自身のディレクトリを取得し、どこから実行されてもパスを解決できるようにする
SCRIPT_DIR=$(dirname "$0")
TARGET_LIST="$SCRIPT_DIR/urls.txt"


# --- 2. 事前チェック ---
# ターゲットリストが存在するかチェック
if [ ! -f "$TARGET_LIST" ]; then
  echo "エラー: ターゲットリスト '$TARGET_LIST' が見つかりません。" >&2
  exit 1
fi


# --- 3. メイン処理 ---
echo "--- サイトのステータスチェックを開始 ---"

while read -r url; do
  # 空行または'#'で始まるコメント行はスキップする
  if [ -z "$url" ] || [[ "$url" == \#* ]]; then
    continue
  fi

  # curlでHTTPステータスコードを取得。-m 5でタイムアウトを5秒に設定。
  status_code=$(curl -m 5 -s -o /dev/null -w "%{http_code}" "$url")

  # ステータスコードに応じて結果を判定・表示
  if [ "$status_code" -eq 200 ]; then
    echo -e "[\e[32mOK\e[0m]       : (\e[32m$status_code\e[0m) $url"
  elif [ "$status_code" -eq 301 ] || [ "$status_code" -eq 302 ]; then
    echo -e "[\e[33mREDIRECT\e[0m] : (\e[33m$status_code\e[0m) $url"
  elif [ "$status_code" -eq 404 ]; then
    echo -e "[\e[31mNOT FOUND\e[0m]: (\e[31m$status_code\e[0m) $url"
  else
    echo -e "[\e[31mERROR\e[0m]    : (\e[31m$status_code\e[0m) $url"
  fi

done < "$TARGET_LIST"

echo "--- チェック完了 ---"

コードの詳細な解説

本スクリプトは、複数のコンポーネントで構成されています。

  • パスの解決 (SCRIPT_DIR=$(dirname "$0"))

    この一行は、スクリプトをどこから実行しても安定して動作させるための重要な記述です。

    1. 問題点: もしこのパス解決の記述がなく、単にurls.txtのように相対パスでファイルを指定した場合、実行場所によって動作が変わるという問題が発生します。
      • 成功例: cd /home/<your_user>/archive/day4 のようにスクリプトと同じ場所に移動してから ./getHTTPStatus.sh を実行した場合。カレントディレクトリにurls.txtが存在するため、スクリプトは正しく動作します。
      • 失敗例: ホームディレクトリ(~)から ./archive/day4/getHTTPStatus.sh を実行した場合。スクリプトはカレントディレクトリであるホームディレクトリ内を基準にurls.txtを探しますが、そこにはファイルが存在しないためNo such file or directoryエラーとなります。
    2. 解決策: この問題を避けるため、スクリプトに自分自身の場所を把握させ、それを基準にurls.txtのパスを解決します。
    3. 実装:
      • $0: まず、特殊変数$0には、実行されたスクリプト自身のパス(例: ./archive/day4/getHTTPStatus.sh)が格納されています。
      • dirname "$0": 次にdirnameコマンドが、$0のパスからファイル名を取り除き、ディレクトリ部分(例: ./archive/day4)だけを抽出します。
      • SCRIPT_DIR=$(...): この抽出したディレクトリパスをSCRIPT_DIRという変数に格納します。
      • TARGET_LIST="$SCRIPT_DIR/urls.txt": 最後に、このディレクトリパスとファイル名を結合し、urls.txtへのフルパスを完成させることで、どこから実行しても安定した動作を保証します。
  • ループ処理 (while read -r url; do ... done < "$TARGET_LIST")
    whileループとreadコマンドを組み合わせ、入力リダイレクト<で指定された$TARGET_LISTの内容を1行ずつ読み込みます。読み込まれた行はurl変数に格納され、ループ内の処理が実行されます。ファイルの終端に達するとループは自動的に終了します。-rオプションは、バックスラッシュを含む行をそのまま読み込むための推奨設定です。

  • 入力フィルタリング (if [ -z "$url" ] ...)
    whileループの冒頭で、読み込んだ行が処理対象かを判定します。-z演算子は変数が空(空行)であるかを、[[ "$url" == \#* ]]は変数が#で始まる(コメント行)かを判定します。continue文は、以降の処理をスキップして次のループサイクルを開始します。

  • ステータスコード取得 (curl ...)
    curlコマンドでHTTPリクエストを送信します。各オプションは以下の機能を持ちます。

    • -m 5: 接続タイムアウトを5秒に設定します。応答のないサイトで処理が滞留するのを防ぎます。
    • -s: 進捗メーターなどのサイレントモードを有効にします。
    • -o /dev/null: レスポンスボディを破棄します。不要な出力を抑制します。
    • -w "%{http_code}": レスポンスヘッダからHTTPステータスコードのみを標準出力に書き出します。
  • 条件分岐 (if [ "$status_code" -eq 200 ])
    $(...)(コマンド置換)でcurlの実行結果を格納した$status_code変数を評価します。数値の比較には-eq(equal)などの算術比較演算子を用います。これにより、ステータスコードに応じたメッセージの表示分けを実現しています。

  • 色付き出力 (echo -e)

    ターミナルに色付きの文字を表示するには、「エスケープシーケンス」という特殊な文字列を使用します。

    1. エスケープシーケンスとは: 画面に表示される文字ではなく、ターミナルに対する「命令」として解釈される文字列のことです。「文字色を赤に変えろ」「カーソルを一行上に動かせ」といった制御が可能で、多くは \e[ から始まります。
    2. 色を付けるには:
      • 書式: \e[色のコードm という形式で色を指定します。
      • 色のコード: 31が赤、32が緑、33が黄色など、色ごとに番号が決まっています。
      • リセット: 一度色を変えると、元に戻すまでその色が適用され続けます。\e[0mという特別なシーケンスを送ることで、文字色をデフォルトに戻すことができます。
    3. スクリプトでの実装:
      echo -e "[\e[32mOK\e[0m]..."
      • echo -e: -eオプションを付けることで、echoはエスケープシーケンスを「命令」として解釈するようになります。
      • \e[32m: これ以降の文字を緑色にする命令です。
      • OK: 緑色で表示される文字列です。
      • \e[0m: 文字色をデフォルトに戻す命令です。これにより、OKという文字だけが緑色になります。

実行方法

スクリプトに実行権限を付与し、実行します。

# 実行権限を付与
chmod 700 /home/<your_user>/archive/day4/getHTTPStatus.sh

# スクリプトを実行
/home/<your_user>/archive/day4/getHTTPStatus.sh

まとめ

#day4では、単純な死活監視から発展させ、より実践的な偵察ツールを作成しました。この過程で、倫理的なテスト対象の選定、実行環境を意識したパスの解決まで意識できた事は大きな収穫だな~と成長を感じる事ができました。

特に、基本的なコマンドをパイプで連携させること、そして引数やエラー処理、パスの解決策を組み込むことで、スクリプトが再利用可能なツールへと昇華するプロセスは、今後の開発において重要な知見となる気がしています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?