Linuxサーバー構築・設定

以前は乗っ取られたサーバーを使っての攻撃が多かったのですが最近はクラウドサービスからの攻撃が増えました。

クラウドサービスを使われると、どんどんIPアドレスを変えてくるので個別のIPアドレスの拒否では対応することができません。

Webサーバーへの攻撃なので、攻撃元のサーバーからはアクセス出来なくても問題ないです。そもそもそういう使い方を許し続けているクラウドサービスなんて危険です。

そういう攻撃があるCIDRをまとめたリストを見つけましたので共有します。

Webサーバーに攻撃があるCIDRリスト(ブラックリスト)

このブラックリストは定期的に更新されているようですので、定期的にダウンロードしファイヤーウォールで拒否してみました。

劇的にサーバーの負荷がさがりました。Webサーバーへのアクセスの殆どがこういう攻撃なんですね。

WordPress

複数のWordPressをkusanagiで管理しているとアクセスログのチェックが面倒です。外部からありえないほどの連続アタックを受けて負荷が上がるとすべてのWordPressのログをチェックする必要がでてきます。

そういう時配下のコマンド。kusanagi内のログを全てチェックしアクセスの多いIPから順に表示してくれます。

CUTOFF=$(date -d '5 minutes ago' +%s)
( for f in /home/kusanagi/*/log/httpd/{access,ssl_access}.log{,.1,.1.gz}; do
    [ -r "$f" ] || continue
    case "$f" in *.gz) zcat -f -- "$f";; *) cat -- "$f";; esac
  done ) \
| awk -v cutoff="$CUTOFF" -v excl='^(153[.]120[.]6[.]98|127[.]0[.]0[.]1)$' '
BEGIN{ split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec",m," ");for(i=1;i<=12;i++)mon[m[i]]=i }
{
  if (match($0, /\[([0-9]{2})\/([A-Za-z]{3})\/([0-9]{4}):([0-9]{2}):([0-9]{2}):([0-9]{2})/, t)) {
    epoch=mktime(sprintf("%04d %02d %02d %02d %02d %02d", t[3]+0, mon[t[2]], t[1]+0, t[4]+0, t[5]+0, t[6]+0))
    if (epoch>=cutoff) {
      lb=index($0,"["); left=(lb>1?substr($0,1,lb-1):$0)
      if (match(left,/([0-9]{1,3}(\.[0-9]{1,3}){3})/,ip)) {
        ipstr=ip[1]
        if (ipstr !~ excl) print ipstr
      }
    }
  }
}' | sort | uniq -c | sort -nr | head

その他

Googleアンケートなどの結果を「毎月末メールで送ってね!」っていうクライアントがたまにいます。しかし、月末は戦場なのです・・・

忘れてしまうと問題ですので、GoogleスプレットシートをExcelに変換してから定期的にメール送信することにしました。

送りたいスプレットシートのメニューから、拡張機能→App Scriptに進み、以下をコピペ。

const FILE_NAME = '件名を入れます';
const TO = '宛先のメールアドレス';
const FROM_ALIAS = '自分のメールアドレス'; // Gmailで別名設定済みであること
const SENDER_NAME = '送信者名';
const SUBJECT = '件名を入れます';
const BODY = `メール本文

メール本文を入れます。

メール本文を入れます

;


function runDaily() {
  if (!isLastDay_(new Date())) return;  // 月末のみ送信
  const file = pickLatestByName_(FILE_NAME);
  if (!file) throw new Error('指定名のファイルが見つかりません: ' + FILE_NAME);

  const xlsxBlob = exportXlsx_(file.getId())
    .setName(`${file.getName()}_${formatYmd_(new Date())}.xlsx`);

  GmailApp.sendEmail(
    TO,
    SUBJECT,
    BODY,
    {from: FROM_ALIAS, name: SENDER_NAME, attachments: [xlsxBlob]}
  );
}

function pickLatestByName_(name) {
  const it = DriveApp.getFilesByName(name);
  let latest = null;
  while (it.hasNext()) {
    const f = it.next();
    if (!latest || f.getLastUpdated() > latest.getLastUpdated()) latest = f;
  }
  return latest;
}

// 置き換え:エクスポート関数
function exportXlsx_(fileId) {
  const url = `https://docs.google.com/spreadsheets/d/${fileId}/export?format=xlsx`;
  const res = UrlFetchApp.fetch(url, {
    headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() },
    muteHttpExceptions: true,
    followRedirects: true,
  });
  if (res.getResponseCode() !== 200) throw new Error('エクスポート失敗: ' + res.getResponseCode());
  return res.getBlob();
}

function isLastDay_(d) {
  const next = new Date(d);
  next.setDate(d.getDate() + 1);
  return next.getDate() === 1;
}

function formatYmd_(d){ return Utilities.formatDate(d, 'Asia/Tokyo', 'yyyyMMdd'); }

後は左側のトリガーで希望のタイミングに設定。

Postfix

SMTP認証を試すボットが増えてきました。そのせいかSMTPソケットがいっぱいになり送信失敗になることがでてきました。

そういうIPは拒否しないといけませんので、上位50(間近24時間)を出力するコマンドです。

journalctl -u postfix -S -24h --no-pager \
| grep -E 'postfix/(submission/)?smtpd.*(SASL .* authentication failed|authentication failed|lost connection after AUTH)' \
| awk '{
    ip=""; s=$0;
    while (match(s, /\[([0-9A-Fa-f:.]+)\]/, m)) { ip=m[1]; s=substr(s, RSTART+RLENGTH) }
    if (ip != "" && ip !~ /^[0-9]+$/) c[ip]++
}
END{
    print "回数\tIPアドレス";
    for (i in c) printf "%d\t%s\n", c[i], i
}' \
| sort -nr | head -50

Linuxサーバー構築・設定

apacheサーバーが突然の高負荷。Timeout時間などを短くしても改善しませんでした。

ACCESSログなどを見ても不審なアクセスはなし。

アクセス数の多いIPアドレスを調べてみました。(153.125から始まるIPは除外)

# awk '{print $1}' /var/log/apache2/access.log | grep -v '^153\.125\.' | sort | uniq -c | sort -nr | head

間近の計測をするためにアクセスログは新しくしました。

結果を見てみるとGoogle Cloudからのアクセス。2分ほどで4000回位のアクセスがあります。

サイトの情報をごっそり抜こうとしているのでしょうか。

該当IPをファイヤーウォールで遮断しました所、ロードアベレージが60超えていたのが、0.9まで落ち着きました。

さて、どうしましょうか・・

このIPが100%原因というわけでもないので、しばらくログを監視する必要があります。10分以内でアクセスの多いIPランキングの出し方。

awk -v d="$(date --date='10 minutes ago' '+%d/%b/%Y:%H:%M')" '
$0 ~ /^\[/ { next }  
{
    gsub(/^\[/, "", $4);
    if ($4 >= d && $1 !~ /^153\.125\./) print $1;
}' /var/log/apache2/access.log | sort | uniq -c | sort -nr | head

WordPress

WordPressの管理画面にログインしようとしても、ログイン画面が表示されずサイトのトップページにリダイレクトされてしまう問題。

最初、ログインURLを変更したのかもと考えましたが違いました。

htaccessを見てもリダイレクト的な記述なし。

原因は、kusanagiのキャッシュでした。以下でログインできるようになりました。

# bcacheを無効化(重要)
kusanagi bcache off ****

ログインできたら、以下で戻しておきます。

kusanagi bcache on ****

キャッシュ破損が原因なんだろうけど・・ここに行き着くまでに結構時間かかりました・・・

MYSQL

2025年~2026年(令和7年~令和8年)祝祭日(正月として、1/2~1/3)が含まれます。以下よりダウンロードいただけます。

2025年~2026年(令和7年~令和8年)祝祭日CSV

2025-01-01 元日
2025-01-02
2025-01-03
2025-01-13 成人の日
2025-02-11 建国記念の日
2025-02-23 天皇誕生日
2025-02-24 振替休日
2025-03-20 春分の日
2025-04-29 昭和の日
2025-05-03 憲法記念日
2025-05-04 みどりの日
2025-05-05 こどもの日
2025-05-06 振替休日
2025-07-21 海の日
2025-08-11 山の日
2025-09-15 敬老の日
2025-09-23 秋分の日
2025-10-13 スポーツの日
2025-11-03 文化の日
2025-11-23 勤労感謝の日
2025-11-24 振替休日
2026-01-01 元日
2026-01-02
2026-01-03
2026-01-12 成人の日
2026-02-11 建国記念の日
2026-02-23 天皇誕生日
2026-03-20 春分の日
2026-04-29 昭和の日
2026-05-03 憲法記念日
2026-05-04 みどりの日
2026-05-05 こどもの日
2026-05-06 振替休日
2026-07-20 海の日
2026-08-11 山の日
2026-09-21 敬老の日
2026-09-23 秋分の日
2026-10-12 スポーツの日
2026-11-03 文化の日
2026-11-23 勤労感謝の日

Apache,nginx

ModPagespeedはウェブサイトのレスポンスを改善するのですが、ModPagespeedを無効化したいディレクトリやファイルが出てきます。

例えば、PhpmyadminはModPagespeedの影響で正常に動作しなくなります。

ModPagespeedを無効化したいディレクトリがある場合は.htaccessに以下を追記。

ModPagespeed off

これだけで、.htaccess配下のディレクトリ全体がModPagespeedが無効化されます。

また、特定のファイルのみ無効化したい場合は以下。

https://www.example.co.jp/?ModPagespeed=off

Linuxサーバー構築・設定,PHP

PHPでmb_strposを使い、XMLフィードから特定の文字列を抽出するコードを書きました。

こちらは、早朝にCronで自動で実行するものです。

手動(コマンド)で実行は問題なし

手動(コマンド)で実行すると期待通りの結果が出てきます。問題なさそうです。

Cronで実行すると失敗が多い

手動(コマンド)で実行すると期待通りの結果になるのに、Cronでは何故かmb_strposで抽出できていない文字列が多数出てきました。

文字コードを明示的に指定する必要がある

サーバーの内部はUTF-8ですし、PHPのソースコードもXMLフィードもUTF-8なので明示的に指定する必要など無いと思っていましたが、Cronなどで実行する場合は指定する必要があるようです。

PHPソースコードで指定

まず、PHPソースコードの上の方に以下を記載しました。

mb_internal_encoding("UTF-8");

このPHPはブラウザからアクセスして実行するのではなく、Cronで実行したいので.htaccessではなくPHPソース内に上記を記載しました。

しかし、Cronで実行すると抽出漏れが発生しています。

次に、mb_strposでも文字コードを指定しました。

mb_strpos(抽出したい文字列, "対象となる文字列", 0, "UTF-8")

これでも駄目です。Cron実行すると同様に抽出漏れが置きます。

正解はCronで文字コードを指定

結果、Cronを記述する時に以下のようにすることで解決しました。

01 9 * * * export LANG="UTF-8"; /usr/bin/php /home/*********.php

なぜCronでは上記のように文字コード指定しないと行けないのかは不明ですが、上記のようにCronで文字コードを指定することで無事解決しました。

Linuxサーバー構築・設定

SiteGuardとSwatchの連携

SiteGuardなどのWAFは不正なアクセスを事前に防いでくれて本当に助かります。

しかし、SiteGuardではアクセス毎の拒否のため、同一IPから同じようなアタックを受けることが少なくありません。

サーバー1台あたり1日数千件のログが残ります。

そのようなIPからのアクセスが、他でまともなアクセスであるわけはないと考えました。

SiteGuardで拒否したログは以下にあります。

/opt/jp-secure/siteguardlite/logs/http/detect.log
/opt/jp-secure/siteguardlite/logs/http/form.log

上記ログファイルをSwatchで読み、iptablesで全てDROPするようにしました。

swatchの設定ファイルは以下の2つ

# logfile /opt/jp-secure/siteguardlite/logs/http/form.log

watchfor /http/
pipe "/usr/local/bin/swatch_action.sh ' ' 3"
threshold track_by=/sshd.*Failed password for invalid user/,type=limit,count=3,seconds=10
# logfile /opt/jp-secure/siteguardlite/logs/http/detect.log

watchfor /http/
pipe "/usr/local/bin/swatch_action.sh ' ' 3"
threshold track_by=/sshd.*Failed password for invalid user/,type=limit,count=3,seconds=10

GoogleのBOTのIPを許可リストへ

上記の設定をしていると、GoogleBOTまで遮断していることが分かりました。

Googleには悪意はない事は明らかですが、何かから辿ってアクセスしてほしくないファイルを読みに着ているようです。

そこで、SiteGuardにGoogleのBOTのIPをホワイトリストとして設定しました。