ボット、クローラのアクセスを除外する方法を調べてみた | Monotalk で、mitchellkrogza/apache-ultimate-bad-bot-blocker存在を知りました。
実際に設定を施して、ボット、クローラのアクセスをブロックしてみました。
結果を以下に記載します。


apache-ultimate-bad-bot-blocker について

Apache でボット、クローラのアクセスをブブロックするための 設定ファイルを提供する github リポジトリです。
Apache 本体の設定ファイル以外にも以下が含まれます。

  • fail2ban Addon
  • Google Analytics のフィルタ定義
  • Google Search Console の リンク否認用のアップロードファイル
  • robots.txt

前提

以下の環境で実施しています。

  • OS

    cat /etc/redhat-release 
    CentOS Linux release 7.5.1804 (Core) 
    

  • Apache の Version

    httpd -V
    Server version: Apache/2.4.29 (CentOS)
    


インストール、設定、定期的な更新

インストール

以下、Apache2.4 向けにインストールスクリプトが用意されています。
apache-ultimate-bad-bot-blocker/install-apacheblocker.sh at master · mitchellkrogza/apache-ultimate-bad-bot-blocker このスクリプトを使用してインストールを実施していきます。

  • スクリプトの取得

    cd ~
    sudo wget https://raw.githubusercontent.com/mitchellkrogza/apache-ultimate-bad-bot-blocker/master/install-apacheblocker.sh 
    chmod +x ~/install-apacheblocker.sh
    

  • 設定値の変更
    スクリプト内のApacheのディレクトリ、設定ファイルの格納ディレクトリを必要があれば書き換えます。

    #Generally /etc/apache2 or /etc/httpd depending on OS
    # Apacheのディレクトリは /etc/httpd
    APACHE_CONF='/etc/httpd'
    #location of Apache blocker files
    BLOCKER_URL="https://raw.githubusercontent.com/mitchellkrogza/apache-ultimate-bad-bot-blocker/master/Apache_${APACHE_VERSION}/custom.d"
    
    # ディレクトリの指定は custom.d
    sudo mkdir -p "${APACHE_CONF}/custom.d"
    

  • スクリプト実行

    sudo ~/install-apacheblocker.sh
    
    幾つかファイルがダウンロードされ、最後に、Manually edit vhost to include globalblacklist.confメッセージが出力されます。
    メッセージの通り、VirtualHost の設定を変更する必要があります。

  • 設定ファイルの説明
    以下の、設定ファイルがダウンロードされました。

    ls -1 /etc/httpd/custom.d/
    ----------------------------------
    bad-referrer-words.conf
    blacklist-ips.conf
    blacklist-user-agents.conf
    globalblacklist.conf
    whitelist-domains.conf
    whitelist-ips.conf
    ----------------------------------
    
    それぞれ内容を説明します。

  • bad-referrer-words.conf
    リファラーspam に設定されているリファラーの文字列を定義するファイルです。
    デフォルトは、コメントのみで末定義で、使用者側で必要があれば追記します。

  • blacklist-ips.conf
    アクセスをブロックする ip が定義されています。
    デフォルトで、幾つかip が定義されています。

  • blacklist-user-agents.conf
    アクセスをブロックする ユーザーエージェントを定義するファイルです。
    デフォルトは、コメントのみで末定義で、使用者側で必要があれば追記します。

  • globalblacklist.conf
    メインの設定ファイルになります。各confファイルはこのファイルから読み込まれています。
    ブラックリストのユーザーエージェント、リファラー文字列が大量に記載されています。

  • whitelist-domains.conf
    アクセスを許可する、domain 文字列を定義します。
    デフォルトはコメントのみです。

  • whitelist-ips.conf
    アクセスを許可する、ip を定義します。
    デフォルトはコメントのみです。

設定

  • VirtualHost定義の編集
    VirtualHost の定義箇所に、globalblacklist.conf Include 記述を追加します。

    <Directory /var/www/site>
            Include custom.d/globalblacklist.conf                                                                                                   
    
            Order Deny,Allow 
            Deny from env=ng_host
            Deny from env=ng_referer
            Deny from env=ng_lang
    
            <IfModule mod_rewrite.c>                
            # domain rewrite rules
            RewriteEngine On                                                                                                                        
    
    複数 VirtualHost があれば複数記述する必要があります。

  • 設定ファイルの読み込み、とテスト
    設定ファイルを読み込み、curl でテストを実施します。

    • ファイル読み込み

      sudo apachectl configtest
      sudo apachectl graceful
      

    • curl でテスト
      ユーザーエージェントを指定して、curl を実行レスポンスコードを確認します。

      curl -A "googlebot" https://www.monotalk.xyz -o /dev/null -w '%{http_code}\n' -s
      200
      
      curl -A "masscan" https://www.monotalk.xyz -o /dev/null -w '%{http_code}\n' -s
      403
      
      curl -I https://www.monotalk.xyz -e http://zx6.ru -o /dev/null -w '%{http_code}\n' -s
      403
      
      レスポンスコード から 適用できていることが確認できました。

定期的な更新

適用はできましたので、globalblacklist.conf更新を定期的に取り込むようにします。
mitchellkrogza/apache-ultimate-bad-bot-blocker更新用のスクリプトが用意されていますので、それを使用します。

  • 更新スクリプトの取得

    sudo wget https://raw.githubusercontent.com/mitchellkrogza/apache-ultimate-bad-bot-blocker/master/update-apacheblocker.sh
    chmod +x update-apacheblocker.sh
    

  • 更新スクリプトを編集
    スクリプト内の、筐体依存、環境依存の箇所を変更します。
    当ブログだと、APACHE_CONFEMAILCURL_TEST_PROTOCOLCURL_TEST_URL_NAME変更しました。

    #Major Apache version e.g. 2.2, 2.4
    APACHE_VERSION='2.4'
    #Directory where bad bot configs are located.
    APACHE_CONF='/etc/apache2/custom.d'
    #location of globalblacklist
    BLACKLIST_URL="https://raw.githubusercontent.com/mitchellkrogza/apache-ultimate-bad-bot-blocker/master/Apache_${APACHE_VERSION}/custom.d/globalblacklist.conf"
    #Address to send update notifications
    EMAIL='email@example.com'
    #Make backup of globalblacklist.conf when updating true or false.
    MAKE_BACKUP=false
    #Run apachectl test before reload true/false
    TEST_BEFORE_RELOAD=true
    #Curl test to ensure blocking still working after reload true/false.
    CURL_TEST_AFTER_RELOAD=true
    #Specify if your site uses http or https
    CURL_TEST_PROTOCOL=http
    #domain name to test against. 
    CURL_TEST_URL_NAME=localhost
    

  • crontab でスケジュール設定
    crontab で定期実行の設定をします。
    私は以下のように設定しました。

    SCRIPT_HOME="/root/script"
    LOG_DIR="/var/log/blog_jobs"
    00 02 * * * /bin/sh $SCRIPT_HOME/update-apacheblocker.sh $>> $LOG_DIR/update-apacheblocker.log # update-apacheblocker
    

  • スクリプトの動作について
    スクリプトの動作は以下のような動きになっています。

    1. globalblacklist.conf が スクリプトの実行環境に存在するか確認し、なければ処理中断。

    2. github リポジトリから globalblacklist.conf を取得、スクリプトの実行環境の globalblacklist.conf と差分をとり、差分がなければ処理中断。

    3. 差分がある場合は、バックアップを取得し globalblacklist.conf を更新。

    4. 更新後に、テストを実施、テストが失敗した場合はメール送付。


ブロック状況をレポートする

悪質なbotのアクセスをNginxでバッサリ切り捨てる記載がありますが、良いボットがブロックされてしまう場合もあります。mitchellkrogza/nginx-ultimate-bad-bot-blockeに、日次でアクセスログを結果をメールで送付するワンライナーのスクリプトが記載されています。
このスクリプトを参考に以下のような、スクリプトを作成しました。

  • monitor_access_log.sh
    #!/bin/sh
    MAIL=your@gmail.com
    LOG_DIR=/var/log/httpd
    
    # Monitoring Daily Referers
    tail -10000 $LOG_DIR/ssl_access_log | awk '$11 !~ /google|bing|yahoo|yandex|www.monotalk.xyz|mutter.monotalk.xyz|monotalk.xyz/' | awk '{print $11}' | tr -d '"' | sort | uniq -c | sort -rn | head -1000 | mail -s "Top 1000 Referers on ssl_access_log for montalk.xyz" $MAIL
    
    # Monitoring Daily User Agents
    tail -50000 $LOG_DIR/ssl_access_log | awk '{print $12}' | tr -d '"' | sort | uniq -c | sort -rn | head -1000 | mail -s "Top 1000 Agents on ssl_access_log for monotalk.xyz" $MAIL
    
    # Monitoring Daily Referers
    tail -10000 $LOG_DIR/access_log | awk '$11 !~ /google|bing|yahoo|yandex|www.monotalk.xyz|mutter.monotalk.xyz|monotalk.xyz/' | awk '{print $11}' | tr -d '"' | sort | uniq -c | sort -rn | head -1000 | mail -s "Top 1000 Referers on access_log for montalk.xyz" $MAIL
    
    # Monitoring Daily User Agents
    tail -50000 $LOG_DIR/access_log | awk '{print $12}' | tr -d '"' | sort | uniq -c | sort -rn | head -1000 | mail -s "Top 1000 Agents on access_log for monotalk.xyz" $MAIL    
    
    上記を cron から 定期実行するようにしました。

レポートの集計

Gmail に送信したレポートのデータ集計のため、Google スプレッドシート で 以下のような、Container Bound Script を作成し、ブロック状況のレポートを集計するようにしました。

function pollMail() {
  var strTerms = "xxxxx@gmail.com AND Top 1000 AND is:unread";
  var numMailMax = 20000; //取得するメール総数  
  var numMail = 500; //1度に取得するメール数
  var myThreads; //条件にマッチしたスレッドを取得、最大500通と決まっている
  var myMsgs; //スレッドからメールを取得する →二次元配列で格納
  var valMsgs;
  var flattenMsgs;

  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("report")
  var i = sheet.getLastRow();

  if(i < numMailMax) {  
    valMsgs = [];
    myThreads = GmailApp.search(strTerms, i, numMail); //条件にマッチしたスレッドを取得、最大500通と決まっている
    myMsgs = GmailApp.getMessagesForThreads(myThreads); //スレッドからメールを取得する →二次元配列で格納

    /* 各メールから日時、送信元、件名、内容を取り出す*/
    for(var j = 0; j < myMsgs.length; j++){
      rows = convertBody2Array(myMsgs[j][0].getBody());
      for (var k = 0; k < rows.length; k++) {
        elem = [];
        elem.push(myMsgs[j][0].getFrom());
        elem.push(myMsgs[j][0].getReplyTo());
        elem.push(myMsgs[j][0].getTo());
        elem.push(myMsgs[j][0].getDate());
        elem.push(myMsgs[j][0].getSubject());
        elem.push(rows[k][0]);
        elem.push(rows[k][1]);
        valMsgs.push(elem);
      }
    }
    /* スプレッドシートに出力 */
    if(valMsgs.length > 0){
      sheet.getRange(i + 1, 1, valMsgs.length, 7).setValues(valMsgs); //シートに貼り付け
    }

    for(var i = 0; i < myThreads.length; i++){
      // 既読にする
      myThreads[i].markRead();
      // ゴミ箱に移動する
      myThreads[i].moveToTrash();
    }
  }  
}

function convertBody2Array(body) {
  var lines = body.split("\r\n");
  var results = [];
  for (var i = 0; i < lines.length; i++) {
    if (lines[i] == "") {
      continue;
    }
    tempLine = lines[i].replace(/\s\s+/g, "");
    results.push(tempLine.split(" "));
  }
  return results;
}


参考

以下、参考になりました。

設定、定期更新、モニタリングまでの設定を行いました。
実運用で使用する場合、変更があったからいきなり更新ではなく、開発環境は自動更新しばらく様子見、本番環境は手動での更新をする等、影響を考えながらの適用になるかと思われました。 以上です。

コメント