タグ: シェルスクリプト Page 2 of 5

ChatGPTによるシェルスクリプト。(対話式のUFWブロック)

概要

不正アクセス対策として役立つUFW。これによる不審なIPアドレスの遮断をもっと効率的に行うようにしてみました。

このスクリプトを書い(てもらっ)た背景

攻撃者は複数のIPアドレスから同時に不正アクセスをしてくるパターンが多々あり、

ufw deny from xxx.yyy.1.1
ufw deny from aaa.bbb.0.2

とやるより、

ufw deny from xxx.yyy.1.0/24
ufw deny from aaa.bbb.0.0/16

と、ネットワークアドレスごと遮断した方が効率的だからです。

前提

  • 既にUFWが稼働していること。
  • サービス指定ではなく、あらゆるアクセスを拒絶するという合意が当事者で取れていること。

ChatGPTへの質問と回答

提示した要件

  1. IPアドレスを尋ねる
  2. IPアドレスそのままか、/24ごとブロックするか、/16ごとブロックするかを0~2で質問(空エンターは0)
  3. 実行前に内容を確認する。yなら続行、nなら中止
  4. 1~2に沿ってufw deny処理を行う
  5. 「ルールを付け加えました。他のIPブロックしますか?」の分岐を出す。yなら1に戻る。nなら次の処理
  6. ufw reloadを行う
  7. 現在のブロック状況を出しますか? yならufw status numberedを表示。nなら終了メッセージを出す

出てきたスクリプト

  • ufw_deny.sh
#!/bin/bash

# ユーザーからIPアドレスとブロック範囲を入力してもらい、
# UFWを使用して指定した範囲のIPアドレスをブロックするスクリプトです。

while true; do
    # IPアドレスを尋ねる
    read -p "ブロックしたいIPアドレスを入力してください: " ip_address

    # ブロック範囲を尋ねる
    read -p "IPアドレスをそのままブロックする場合は0、/24ごとにブロックする場合は1、/16ごとにブロックする場合は2を入力してください(空エンターは0): " block_option

    # 入力が空の場合はデフォルトで0(IPアドレスそのまま)とする
    block_option=${block_option:-0}

    # ブロック範囲を計算
    case $block_option in
        1) block_cidr="/24" ;;
        2) block_cidr="/16" ;;
        *) block_cidr="" ;;
    esac

    # ネットワークアドレスに変換
    if [[ -n "$block_cidr" ]]; then
        if [[ $block_option == "1" ]]; then
            # /24で区切る場合
            network_address=$(echo $ip_address | cut -d'.' -f1-3)
            cidr_notation="$network_address.0$block_cidr"
        elif [[ $block_option == "2" ]]; then
            # /16で区切る場合
            network_address=$(echo $ip_address | cut -d'.' -f1-2)
            cidr_notation="$network_address.0.0$block_cidr"
        fi
    else
        cidr_notation="$ip_address"
    fi

    # 設定内容を確認する
    echo "以下の内容でブロックを行います:"
    echo "IPアドレス: $cidr_notation"

    read -p "よろしいですか?(y/n): " confirm

    # ユーザーが処理を続行しない場合、スクリプトを終了する
    if [[ $confirm != "y" && $confirm != "Y" ]]; then
        echo "処理を中止しました。"
        exit 0
    fi

    # ブロックルールをufwに追加
    sudo ufw deny from "$cidr_notation"

    # エラーがあった場合はエラーメッセージを表示して終了
    if [[ $? -ne 0 ]]; then
        echo "ERROR: 不正なソースアドレス"
        exit 1
    fi

    echo "ルールを付け加えました。"

    # 他のIPをブロックするか尋ねる
    read -p "他のIPをブロックしますか?(y/n): " continue_blocking
    if [[ $continue_blocking != "y" && $continue_blocking != "Y" ]]; then
        break
    fi
done

# ufwを再読み込みする
sudo ufw reload

# ブロック状況を確認するか尋ねる
read -p "現在のブロック状況を確認しますか?(y/n): " check_status
if [[ $check_status == "y" || $check_status == "Y" ]]; then
    sudo ufw status numbered
fi

# 終了メッセージを出す
echo "IPブロックを終了しました。"

このスクリプトを

chmod +x ufw_deny.sh

とすれば作成は完了です。

スクリプトの動き

./ufw_deny.sh
ブロックしたいIPアドレスを入力してください:
# IPアドレスを入力します

IPアドレスをそのままブロックする場合は0、/24ごとにブロックする場合は1、/16ごとにブロックする場合は2を入力してください(空エンター
は0): 
# IPアドレスのレンジを
# IPアドレスそのまま:0
# /24(255.255.255.0)で区切る:1
# /16(255.255.0.0)で区切る:2

IPアドレス:
よろしいですか? (y/n):
# 先ほどのIPを表示します。ネットワークアドレスの場合は、/24 /16で区切って表示します。
# 内容を確認します。
# y を入力後、ufwの処理が走ります。一般ユーザーの場合はsudoパスワードが訊かれます。

ルールを追加しました
ルールを付け加えました。
# ufw deny from (入力したIP/NWアドレス)を実行します。

他のIPをブロックしますか?(y/n): 
# 続行するかを訊きます。yの場合はIPアドレスから始まります。

ファイアウォールを再読込しました
現在のブロック状況を確認しますか?(y/n):
# yの場合は sudo ufw status numberedを実行します。

と、

  • IPアドレス→ネットワークアドレスへの変換
  • UFW DENYに追加
  • UFWルールの読込
  • 設定後のルール表示

まで一元管理してくれます。

IPアドレスをネットワークアドレスにした上での洗い出し。

この記事の続きです。

シェルスクリプトによって、スペースで区切られただけのIPアドレスをリスト化することに成功したので、

192.168.1.2
192.168.1.3
172.28.1.3
172.28.1.5
...

といったIPアドレスのリストを

2, 192.168.1.0/24
2, 172.28.1.0/24

というように、/24で区切ったアドレス帯と件数を表示するスクリプトをchatGPTに作成いただきました。

スクリプト内容

  • network-address-count.sh
#!/bin/bash

# ファイルのパスを引数として受け取る
file_path=$1

# ファイルが存在するかを確認する
if [ ! -f "$file_path" ]; then
    echo "指定されたファイルが見つかりません。"
    exit 1
fi

# ネットワークアドレスごとにIP数をカウントして表示
awk -F. '{print $1"."$2"."$3".0"}' $file_path | sort | uniq -c | while read count address; do
    echo "$count, $address/24"
done

あとは実行権を付与します。

スクリプトの動き

./network-address-count.sh /path/to/log/file
# 上述のリンク先でのログファイルを指定します。

結果

2, 71.6.146.0/24
1, 71.6.158.0/24
1, 71.6.199.0/24
11, 71.6.232.0/24

のように、攻撃をしてきたIPアドレスをネットワークアドレスごとにカウントすることができました。

これは、今後の防御手段をしる手段として有効です。

Fail2banが弾いたIPアドレスの洗い出し。

はじめに

不審なアクセスを弾いているfail2ban。

sudo fail2ban-client status sshd

と実行することでBANしたIPのリストが出てきます。

ただ、それは

aaa.bbb.ccc.ddd AAA.BBB.CCC.DDD

のようにスペース区切りとなっていて読むのが大変。

そこで、IPアドレスを一覧表示して可読性を高めるようにします。

前提

環境

  • Ubuntu 20.0.4
  • fail2ban 0.11.1

sshdの設定

  • /etc/fail2ban/jail.local
(抜粋)
[sshd]
enabled=true
filter=sshd
mode=normal
port=22
protocol=tcp
logpath=/var/log/auth.log
maxretry=3
bantime=-1
# ログインが3回失敗したIPアドレスを猶予期間無しに永久追放します
ignoreip = 127.0.0.0/8 ::1 #その他、除外IPを必要に応じて

enabled=true filter=sshd mode=normal port=22 protocol=tcp logpath=/var/log/auth.log maxretry=3 bantime=-1 # ログインが3回失敗したIPアドレスを猶予期間無しに永久追放します ignoreip = 127.0.0.0/8 ::1 #その他、除外IPを必要に応じて

さっくりとした手順

  1. fail2banが弾いているIPをログファイルに出力するためのスクリプトを作成します。
  2. 出力したファイルのディレクトリを作成します。
  3. 動作を確認します。
  4. cronで自動実行されるようにします。
  5. 出力したログファイルをローテーションする設定を追加します。

スクリプト作成

※教義・信仰に沿ったエディタで作成してください。※

  • fail2ban-ssh-sort.sh
#!/bin/bash

# ログの格納場所を指定
log_directory="/path/to/log/directory"

# fail2ban-client status sshdを実行し、IPアドレスを取得
ip_addresses=$(fail2ban-client status sshd | awk 'BEGIN {RS="[ \\t]+"} {if ($1 ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/) print $1}')

# IPアドレスを一行ずつ表示してソートし、csvファイルに出力
echo "$ip_addresses" | sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4 > "$log_directory/sorted_ips.csv"
  • 実行権付与
chmod +x fail2ban-ssh-sort.sh

ログ出力ディレクトリを作成

sudo mkdir /var/log/fail2ban-ips
# 上記のスクリプトで指定したディレクトリを指定します。

動作確認

  • スクリプト実行※要管理者権限※
sudo bash fail2ban-ssh-sort.sh
  • ファイル出力確認
cat /var/log/fail2ban-ips/sorted_ips.csv
# IPアドレスの出力がされていれば成功です。

cron自動実行

  • cron編集
sudo crontab -e -u root
  • 編集内容
0 3 * * * /path/to/directory/fail2ban-ssh-sort.sh
# スクリプトを格納したディレクトリを絶対パスで指定します
# この例では毎日午前3時に実行します

ログローテーション設定

※管理者権限で、教義・信仰に沿ったエディタで作成してください。※

  • /etc/logrotate.d/fail2ban-ssh-sort
/var/log/fail2ban-ips/sorted_ips.csv {
# スクリプトで指定したログ格納ディレクトリ/ログファイルを指定
    daily
    rotate 3
    compress
    missingok
    notifempty
}

ローテーション設定確認

sudo logrotate -d /etc/logrotate.d/fail2ban-ssh-sort

エラーがなければ成功です。

これで、加工しやすいCSVファイルで不審なログインのアクセス元を確認することが可能になりました。

update-mot.dに仕込むスクリプト(SSL証明書の有効期限確認)

概要

2023年10月現在、自分のサイトの証明書はLet's Encryptのワイルドカード証明書を利用しています。
性質上、有効期限が90日のため「いつ頃が有効期限か」を確認するためのスクリプトをChatGPTに書いてもらいました。

スクリプトが動く要件

  • サーバがインターネットに接続されていること。
  • Rubyがインストールされていること。
  • Opensslがインストールされていること。

指示した内容

  1. 変数で指定したURLにopensslで接続
  2. 証明書の有効期限を読み取る
  3. 「サイト ○○ の有効期限はyyyy/mm/dd です。残り○日です」と表示する

返ってきたスクリプト

  • ssl_cecker.rb
  • URLは必要に応じて変更してください。
require 'openssl'
require 'socket'
require 'date'
require 'uri'  # URIモジュールを追加

# 変数で指定したURL
url = "https://hoge.example.com"

# 変数で指定したURLに接続して証明書の有効期限を取得するメソッド
def get_certificate_expiry_date(url)
  uri = URI.parse(url)
  hostname = uri.host

  begin
    # SSL接続を確立
    tcp_client = TCPSocket.new(hostname, 443)
    ssl_context = OpenSSL::SSL::SSLContext.new
    ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_client, ssl_context)
    ssl_socket.hostname = hostname
    ssl_socket.connect

    # 証明書の有効期限を取得
    cert = ssl_socket.peer_cert
    expiration_date = DateTime.parse(cert.not_after.to_s)
    days_remaining = (expiration_date - DateTime.now).to_i

    return expiration_date, days_remaining
  rescue => e
    return nil, e.to_s
  ensure
    ssl_socket&.close
    tcp_client&.close
  end
end

# メイン処理
def main(url)
  expiration_date, days_remaining = get_certificate_expiry_date(url)

  if expiration_date
    formatted_date = expiration_date.strftime("%Y/%m/%d")
    puts "サイト #{url} の有効期限は #{formatted_date} です。残り #{days_remaining} 日です。"
  else
    puts "証明書の取得に失敗しました: #{days_remaining}"
  end
end

# メイン処理を呼び出し
main(url)

スクリプト実行例

ruby ssl_checker.rb

サイト https://hoge.example.com の有効期限は 2024/01/12 です。残り 88 日です。

motodへの登録

これはサーバ管理の度に確認したいので、motdに以下のスクリプトを管理者権限で配置します。

  • ファイル名
/etc/update-motd.d/99-custom-motd
  • スクリプト内容
#!/bin/bash
ruby /path/to/directory/ssl_cecker.rb
  • 実行権付与
sudo chmod +x /etc/update-motd.d/99-custom-motd

motd登録確認

任意のターミナルクライアントでSSH接続後、

サイト https://hoge.example.com の有効期限は 2024/01/12 です。残り 88 日です。

と表示されればうまくいっています。

備考

  • 今回のChatGPTへの伺いはほぼ一発でした。
  • 対話型やテキストを読み込むなども機を見て実装させたいです。

ChatGPTによるシェルスクリプト。(DNSレコード問い合わせ-2-)

続・DNSレコードを問い合わせるシェルスクリプト

このスクリプトを発展させました。

スクリプト内容

  • dns.sh
#!/bin/bash

# ドメイン名の入力を求める
read -p "ドメイン名を入力してください: " query

# 入力が未設定の場合の処理
if [ -z "$query" ]; then
    # デフォルトのDNSで問い合わせを行う
    default_dns=$(dig +short A +short myip.opendns.com @resolver1.opendns.com)
    echo "デフォルトのDNS: $default_dns"
else
    # ユーザーが指定したDNSで問い合わせを行う
    read -p "問い合わせるDNSを指定してください(空白Enterの場合はデフォルトのDNSを使用します): " dns_server

    if [ -z "$dns_server" ]; then
        dns_server="@resolver1.opendns.com"
    else
        dns_server="@$dns_server"
    fi

    # Aレコードの問い合わせ
    a_record=$(dig +short A $query $dns_server)
    if [ -z "$a_record" ]; then
        a_record="未設定です"
    fi

    # TXTレコードの問い合わせ
    txt_record=$(dig +short TXT $query $dns_server)
    if [ -z "$txt_record" ]; then
        txt_record="未設定です"
    fi

    # MXレコードの問い合わせ
    mx_record=$(dig +short MX $query $dns_server)
    if [ -z "$mx_record" ]; then
        mx_record="未設定です"
    fi

    # レコードの問い合わせの種類を選択するプロンプトを表示
    echo "問い合わせるレコードの種類を選んでください:"
    echo "1. Aレコード"
    echo "2. TXTレコード"
    echo "3. MXレコード"
    echo "4. すべてのレコード"

    # レコードの種類をユーザーに選択させる
    read -p "選択してください (1, 2, 3, または 4): " record_type

    # ユーザーの選択に応じて問い合わせ結果を表示
    case $record_type in
        1)
            # Aレコードの問い合わせ結果を表示
            echo "Aレコード: $a_record"
            ;;
        2)
            # TXTレコードの問い合わせ結果を表示
            echo "TXTレコード: $txt_record"
            ;;
        3)
            # MXレコードの問い合わせ結果を表示
            echo "MXレコード: $mx_record"
            ;;
        4)
            # すべてのレコードを表示
            echo "Aレコード: $a_record"
            echo "TXTレコード: $txt_record"
            echo "MXレコード: $mx_record"
            ;;
        *)
            # 無効な選択が入力された場合のエラーメッセージ
            echo "無効な選択です。1, 2, 3, または 4 を入力してください。"
            ;;
    esac
fi

追加した内容

「どこのDNSサーバに問い合わせを行うか」です。

  • ローカルDNSに問い合わせたい場合
  • 冗長化を組んでいるDNSのセカンダリーに反映されているかを確認したい場合
  • 世界的なDNSでもレコードが引けるか

を確認するケースが多々あったので、これを追加です。今回ChatGPTが指定したのはopendnsなので、

DNSサーバ名主なIPアドレス
Google Public DNS8.8.8.8
Cloudflare DNS1.1.1.1
Quad99.9.9.9
Level 3 DNS4.2.2.1

といったDNSサービスを変数に入れればデフォルトを変えてくれます。

ChatGPTによるシェルスクリプト。(DNSレコード問い合わせ)

運用時に多用するdigコマンド。オプション入力が面倒なので、スクリプトをChatGPTに書いてもらいました。

スクリプトの動き

  1. ドメイン名をプロンプトで入力させる。
  2. どのレコードを問い合わせるのかをプロンプトで表示。
  3. 2の結果に従い、結果を返す。

スクリプトの内容

  • dns.sh
#!/bin/bash

# ドメイン名の入力を求める
read -p "ドメイン名を入力してください: " query

# 入力が未設定の場合の処理
if [ -z "$query" ]; then
    echo "エラー: ドメイン名が設定されていません。"
else
    # Aレコードの問い合わせ
    a_record=$(dig +short A $query)
    if [ -z "$a_record" ]; then
        a_record="未設定です"
    fi

    # TXTレコードの問い合わせ
    txt_record=$(dig +short TXT $query)
    if [ -z "$txt_record" ]; then
        txt_record="未設定です"
    fi

    # MXレコードの問い合わせ
    mx_record=$(dig +short MX $query)
    if [ -z "$mx_record" ]; then
        mx_record="未設定です"
    fi

    # レコードの問い合わせの種類を選択するプロンプトを表示
    echo "問い合わせるレコードの種類を選んでください:"
    echo "1. Aレコード"
    echo "2. TXTレコード"
    echo "3. MXレコード"
    echo "4. A / TXT / MXレコード"

    # レコードの種類をユーザーに選択させる
    read -p "選択してください (1, 2, 3, または 4): " record_type

    # ユーザーの選択に応じて問い合わせ結果を表示
    case $record_type in
        1)
            # Aレコードの問い合わせ結果を表示
            echo "Aレコード: $a_record"
            ;;
        2)
            # TXTレコードの問い合わせ結果を表示
            echo "TXTレコード: $txt_record"
            ;;
        3)
            # MXレコードの問い合わせ結果を表示
            echo "MXレコード: $mx_record"
            ;;
        4)
            # A / TXT / MXレコードを表示
            echo "Aレコード: $a_record"
            echo "TXTレコード: $txt_record"
            echo "MXレコード: $mx_record"
            ;;
        *)
            # 無効な選択が入力された場合のエラーメッセージ
            echo "無効な選択です。1, 2, 3, または 4 を入力してください。"
            ;;
    esac
fi

入力後、

chmod +x dns.sh

で実行権を付与。

実行例

./dns.sh 
ドメイン名を入力してください: google.co.jp
問い合わせるレコードの種類を選んでください:
1. Aレコード
2. TXTレコード
3. MXレコード
4. A / TXT / MXレコード
選択してください (1, 2, 3, または 4): 4
Aレコード: 142.250.199.99
TXTレコード: "v=spf1 -all"
MXレコード: 0 smtp.google.com.

今後足したい機能

  1. 問い合わせ先のDNSサーバの変数指定
  2. レコードを増やす
  3. 空エンター時の挙動

など、伸ばす余地はあります。

曜日を判別するmot_d。(Ubuntuシェルスクリプト)

概要

サーバにターミナル経由でログインした際に表示されるメッセージ、motd(Message of the Day)。

「特定の日時・曜日をを判別して、その条件を満たしたときにメッセージを表示することはできないか?」

ということでシェルスクリプトを書いてみました。

動作を確認した環境

  • Ubuntu 20.04

スクリプト内容

  • /etc/update-motd.d/02-Friday_Check(または既存のupdate-motdに追記します)

※管理者権限で追記する必要があります。

#!/bin/bash

# 現在の言語ロケールを保存します。
original_locale=$(locale | grep "LANG=" | cut -d= -f2)

# ロケールを英語に修正します。
export LANG="en_US.UTF-8"

# ロサンゼルス(カリフォルニア)の曜日を調べます。
day_of_week=$(TZ="America/Los_Angeles" date +"%A")

# 金曜日だった場合のみメッセージを表示します。
if [ "$day_of_week" == "Friday" ]; then
    echo "Today is Friday in California."
fi

# 元の言語ロケールに戻します。
export LANG="$original_locale"

追記後、

sudo chmod +x /etc/update-motd.d/02-Friday_Check

として、実行権限を付与してください。(既存スクリプトに追記する場合はその限りではありません)

スクリプトの動き

サーバにログインした際に

  1. 現在の言語ロケールを保存します。
  2. 言語ロケールを英語に修正します。(曜日の変数をFridayに固定しているため)
  3. タイムゾーンをチェックして、カリフォルニアで金曜日かどうかを判別します。
  4. PST(太平洋標準時)/PDT(太平洋夏時間)で金曜日の時間帯に
  5. Today is Friday in California. を表示します。
  6. 最後に、保存された言語ロケールへと戻します。

まとめ

今回は単に文字列を判別するだけ。ですが、月末時の処理や保守更新などの応用が利きそうです。

Linux:シェルスクリプトで天気表示。

概要

Linuxのコマンドラインで天気を知りたい状況があったので、簡単なスクリプトを書きました。

前提

  • Linux機がインターネットに繋がっていること。
  • ansiweatherがインストールされていること。

Ubuntuでのansiweatherインストール方法

以下のコマンドを実行します。

sudo aptitude install ansiweather
# 筆者の好みでaptitudeを用いています。必要に応じてaptを使ってください。

スクリプト

  • getweather.sh

以下の内容を教義・信仰に沿ったエディタで記載します。

#!/bin/bash

# ユーザーに都市名を尋ねる
echo "都市名を入力してください:"
read city

# ansiweatherコマンドを実行して天気情報を表示
echo "ansiweatherの情報:"
ansiweather -l "$city"

# curlコマンドを使用してwttr.inから天気情報を表示
echo "wttr.inの情報:"
curl wttr.in/"$city?lang=ja"
  • 実行権限付与
chmod +x getweather.sh

スクリプトの動き

  • スクリプト実行
./getweather.sh
  • 実行結果

都市名を入力します。(Tokyo, Londonなど)

入力後、以下のように1行で現在の天気の概要、アスキーアートで3日の予報が出てきます。

Torの出口ノードリストからのWebアクセスを遮断する設定。(Mod_Securityとの連携)

こちらの記事の続きとなります。

Torの出口ノードリストを抽出するスクリプトと、既に稼働済みの不審なIPアドレスをブロックするスクリプトを連携させます。

前提

こちらにあるように、

  • Mod_Security導入済み
  • エラー検知時にブロックする設定をバーチャルサイトに設定済み

となります。

また、先日の記事である、

  • Tor出口ノードリストを抽出スクリプトがCron化されているものとします。

スクリプト

  • negativelist.sh
#!/bin/bash

# 読み込むログのディレクトリとファイル名を変数指定
log_dir="/var/lib/redmine/log"
log_file="error.log"

# シェルスクリプトから抽出したTorの出口ノードリストを指定
tor_list_dir="/path/to/directory"  # tor_list.txtのディレクトリを指定
tor_list_file="sorted_ip_addresses$(date +%Y%m%d).txt"

# 除外するIPアドレスをファイルで指定
# 自分のNWなど、偽陽性を防ぐために記載したアドレスのリストです。
exclude_ips_file="/path/to/directory/exclude_ips.txt"

# ログファイルからIPアドレスを抽出して重複を排除し、ファイルに保存します。
cd "$log_dir"
awk 'match($0,/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/) { print substr($0, RSTART, RLENGTH) }' "$log_file" | sort -u > "$log_dir/suspicious_ip/suspicious_ip.$(date +%Y%m%d)"
chown www-data:www-data "$log_dir/suspicious_ip/suspicious_ip.$(date +%Y%m%d)"

# 過去のIPアドレスを読み込んで重複を排除し、ファイルに保存します。
cat "$log_dir/suspicious_ip/suspicious_ip."2* | sort -u > "$log_dir/suspicious_ip_all.txt"
chown www-data:www-data "$log_dir/suspicious_ip_all.txt"

# 新たにリストに書き起こすと同時にTorの出口ノードリストを読み込んで重複を排除し、negativelist.txtに追加します。
cat "$log_dir/suspicious_ip_all.txt" "$tor_list_dir/$tor_list_file" | sort -u > /etc/apache2/sites-available/negativelist.txt

# 除外するIPアドレスをファイルから削除します。
while IFS= read -r exclude_ip; do
  sed -i "/$exclude_ip/d" /etc/apache2/sites-available/negativelist.txt
done < "$exclude_ips_file"

# Apacheを再起動します。
systemctl restart apache2.service

あとはこちらをroot権限でCron登録。

設定後の動き

以下のように動きます。

  1. 指定のログファイルからエラーとなったIPアドレスのみを抽出します。
  2. 全てのIPアドレスを統合し、重複を排除してsuspicous_ip_all.txtとして抽出します。
  3. suspicous_ip_all.txtとtor_list_fileを統合し、negativelist.txtとして作成します。(これらのIPアドレスからのアクセスに対しては全て403を返します)
  4. negativelist.txtから除外IPアドレスを取り除きます。
  5. Webサービスを再起動します。

ひとまず、WAFとの連携ができました。

IPアドレス抽出スクリプト。(Torの出口リスト)

必要があったので、スクリプトを書きました。

スクリプトの動き

  1. 公開されているTor出口リストをダウンロード。
  2. IPアドレスのみを抽出。
  3. 重複を排除してソート。

スクリプト内容

  • tor_exit_hodes.sh
#!/bin/bash

# 出口ノードリストのURL
TOR_EXIT_LIST_URL="https://check.torproject.org/exit-addresses"

# ダウンロード先ファイル名
OUTPUT_FILE="exit_nodes.$(date +%Y%m%d).txt"

# curlを使用してリストをダウンロード
curl -o "$OUTPUT_FILE" "$TOR_EXIT_LIST_URL" 

##curl -o "$OUTPUT_FILE" "$TOR_EXIT_LIST_URL" >/dev/null 2>&1
##cron処理などを行う場合はこちらを使います。

# ダウンロードが成功したかどうかを確認
if [ $? -eq 0 ]; then
  echo "Tor出口ノードリストをダウンロードしました。ファイル: $OUTPUT_FILE"
else
  echo "ダウンロード中にエラーが発生しました。"
  exit 1
fi

# IPアドレスのみを抽出し、ソートして出力
awk '/^ExitAddress/ {print $2}' "$OUTPUT_FILE" | sort | uniq | tee sorted_ip_addresses$(date +%Y%m%d).txt 

##awk '/^ExitAddress/ {print $2}' "$OUTPUT_FILE" | sort | uniq > sorted_ip_addresses$(date +%Y%m%d).txt >/dev/null 2>&1
##cron処理などを行う場合はこちらを使います。

これで、IPアドレスのみが抽出されますので、apache/nginxやFiwawall/WAFとの連携が容易になります。

今後の動き

  1. Cron化
  2. Mod_Securityとの連携

などやっていきます。

Page 2 of 5

Powered by WordPress & Theme by Anders Norén