タグ: Linux Page 1 of 32

攻撃者の「影響度」を確認するAbusedIPDB。

Webサーバ管理者を悩ませる不正アクセス。これが「常習者か」を見極めるために有用なサイトと、その使い方を説明します。

そもそも論として「攻撃者は常習性があるか?」

これは明確に、明白にYESと大文字で答えます。彼らは無差別に、利用できるサーバを機械的に(または悪意を持って)攻撃します。

そのような攻撃者のアクセス元をユーザーの善意の報告の元でDB化しているサイトが本稿で紹介するAbuseIPDB.comです。

AbuseIPDB.comとは

AbuseIPDBは、悪意のあるIPアドレスを報告・検索できるサービスで、サイバー攻撃対策に活用されます。IPの信頼性を確認したり、APIで自動分析も可能です。(この自動分析は筆者は利用経験無し)

主に以下のような用途で利用されます。

  • IPアドレスの信頼性確認:検索ボックスにIPを入力すると、過去の報告履歴や「Abuse Confidence Score(悪用の可能性)」が表示されます。
  • 不審なIPの報告:攻撃やスパムを検知した際に、行動の種類(例:SSH brute-force、DDoS)を指定して報告できます。
  • APIによる自動化:無料アカウントを作成すれば、APIキーを取得してスクリプトやセキュリティツール(例:Fail2Ban)と連携可能なようですが、筆者は未実施。

AbuseIPDB.com レポートサンプル

では、実際に上記のWebサイトを見てみましょう。

https://www.abuseipdb.com

にアクセスします。

そして、エラーログなどから攻撃の兆候があったIPアドレスを入力します。

その結果はこちらです。(IPアドレスや報告者はダミーデータ。そして、レポートは日本語にしています)


IPアドレス 198.51.100.123 はAbuseIPDBのデータベースに登録されていました!

このIPアドレスは 10,751回 報告されています。不正利用の信頼スコアは 100% です。

  • 不正利用の信頼スコア: 100%
  • ISP: Example Hosting Ltd.
  • 用途: データセンター/Webホスティング
  • ASN: AS65500
  • ホスト名: scan-server-01.example.com
  • ドメイン名: examplehosting.com
  • : some country
  • 都市: some city

最近の不正利用レポート (一部抜粋)

報告者 (国籍)タイムスタンプ (UTC)内容カテゴリ
🇫🇷 Reporter Alpha2025-10-23 01:16:03Firewall blocked port scan attempt [src port: 44256, dst port: 267]ポートスキャン
🇬🇧 User Bravo2025-10-23 00:36:43SSH login attempt failed.ブルートフォース, SSH
🇩🇪 Security Team C2025-10-22 22:49:36FW-PortScan: Traffic Blocked srcport=43104 dstport=22ポートスキャン, ハッキング, SSH
🇺🇸 Monitor Delta2025-10-22 21:46:12Connection attempt to tcp/7707ポートスキャン
🇺🇸 Honeypot Echo2025-10-22 20:02:02Unauthorized activity to TCP port 25.ポートスキャン
🇺🇸 Monitor Foxtrot2025-10-22 16:28:59Connection attempt to tcp/25ポートスキャン

ここから分かること

  • あなたのサーバに攻撃した奴は、他のサーバにも万単位で攻撃しているパターンが極めて高い。
  • あなたのサーバに問題があるのではなく、あなたのサーバに問題があることを期待しての不正アクセスを試行する輩の問題である。

従って:これらのIPアドレスをブロックすることに遠慮も躊躇も必要ありません。彼らは閲覧者では断じてありません。単なる攻撃者です。

攻撃者には

  • 交渉の余地を与えない
  • 「攻撃が有効である」という成功体験を与えてはなりません。そして、一度でも要求を呑めば更なる被害を生みます。
  • 聞く耳を持たない
  • 上記に同じです。「騒げば主張を聞いてもらえる」などという考えは許されません。
  • 名前を与えない
  • 攻撃者は見返り以上に「この騒ぎを起こした」という一種の名誉を求めます。その名誉となる名前は剥奪します。

の三原則の元、iptables(ufw) や筆者が前述したblacklistなどに放り込みましょう。

まとめ

このようにAbuseIPDBを確認することで、特定のIPアドレスがどのような不正行為を行っているか、世界中のユーザーからの報告を通じて把握することができます。

信頼スコアや報告回数、ISP、国などの情報から、そのIPアドレスをブロックすべきかどうかを判断する重要な材料になります。

不正アクセスは確かに脅威ですが、こちらのAbusedIPDBのような善意の協力者がいるという強みがあります。

これらを利用・貢献するというのも、自身が管理するWebサーバを攻撃から守る手段の一つです。

Redmineインストール後に行う避難訓練(Redmineの再作成)

ここでは、Redmineを立ち上げ、基本的な設定を済ませた人が行っていただきたい「避難訓練」について述べます。

環境

  • Ubuntu 22.04
  • 動かしていたRedmine:5.1
  • Apache 2.4 / mod-passangerでRubyアプリを使用(Ruby 3.2.3)
  • MySQL 8.0.3

避難訓練の内容

  • 最終的な目的
    • Redmineの再構築
  • 避難訓練で行うこと
    • DBバックアップ
    • 「稼働環境のDBの削除」
    • Redmine再構築
    • バックアップしたDBの流し込み
    • 再構築確認

何故このタイミングで?

「他の人が誰も使っていない状態だから」に尽きます。

自分以外の誰かが使っている中で

  • 障害が発生してしまった状況でいきなりリストアを行う
  • 最初にリストア訓練を行っている

状況下、安心できるのはどちらでしょうかという単純な問題。

そして、Redmineは古くから(2006年)使われているため

  • プラグインの相性問題
  • チューニングミス
  • 設定変更

などで簡単にぶっ壊れます。筆者は2桁単位で壊してます。なので「壊れないようにする」ではなく「壊れてもすぐ元に戻せる」心構えがRedmineは特に重要です。

作業前のチェック

以下が必須です。

  • [ ] 慎重な心構え
  • [ ] ある程度リラックスできて集中できる環境。

さっくりとはならない手順

  1. スナップショットのバックアップ (推奨)
  2. DBのバックアップ
  3. redmineのディレクトリを一度mvでリネームしてバックアップ。
  4. apache停止
  5. redmineのDBを消す。
  6. redmineのDBを新たに作る。(ユーザは全て権限があるので問題なし)
  7. apache再開
  8. ディレクトリを再作成。
  9. themesとpluginを再配置。
  10. themesとpluginを再配置した状態でDBマイグレーション。
  11. DBリストア。
  12. 動作確認。

システム全体のバックアップ(推奨)

万一に備え、システム全体のバックアップを取ることを推奨します。AWSや仮想サーバ等の場合は、インスタンスをまるごとバックアップしておくと良いでしょう。

mysqldumpによるDBバックアップ

  • 保存ディレクトリに移動
cd /hoge

任意のバックアップディレクトリを指定します。

mysqldump -h localhost -u redmine -p --no-tablespaces --single-transaction redmine > redmine_backup.$(date +%Y%m%d).sql

DB名やDBユーザは自分の環境に合わせます。

データ退避

  • Redmineのルートディレクトリの上に移動
cd /home/www-data && pwd

Redmineが格納されているディレクトリの親ディレクトリに移動します。筆者環境は/home/www-dataなので、自分の環境に合わせます。

  • ディレクトリがあることを確認
ls -ld redmine

退避対象のディレクトリがあることを確認します。

  • リネームして退避
sudo mv redmine redmine_$(date +%Y%m%d)
  • 退避確認
ls -ld redmine_$(date +%Y%m%d)

ファイルがあることを確認します。

apache停止

ここでWebサービスを停止するのは、DBを削除するためです。

  • apache停止前確認
systemctl status apache2.service

active(running)を確認します

  • apache停止
sudo systemctl stop apache2.service
  • apache停止後確認
systemctl status apache2.service

inactive(dead)を確認します

DB削除と再作成

この作業は慎重に行って下さい。

  • 管理者権限でmysqlにログイン
sudo mysql -u root -p
  • DB確認
SHOW DATABASES;

redmineのDBがあることを確認します。

  • RedmineのDBを削除
DROP DATABASE redmine;

DB名は再確認してください。この作業前に3回ほど深呼吸して、何か飲み物をゆっくり飲んでからにしましょう。

  • RedmineのDB削除確認
SHOW DATABASES;

RedmineのDBがないことを確認します。

  • 空のDB再作成
CREATE DATABASE redmine CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

同じDBを作り直します。

  • 空のDB再作成確認
SHOW DATABASES;

作成したDBがあることを確認します。

EXIT
  • redmineDBユーザでログイン
mysql -u redmine -p

DB再作成前、redemineのDBにアクセスできるユーザー名です。上記操作はDBの削除は行いましたがユーザーの削除は行っていません。従って、削除前のユーザー名とパスワードでRedmineのDBにアクセス可能です。

  • DB確認
SHOW DATABASES;

作成したDBがあることを確認します。

EXIT

ディレクトリ退避

  • Web格納ディレクトリに移動
cd /home/www-data && pwd

自分の環境に合わせます。

  • 退避前ディレクトリ確認
ls -ld redmine

Redmine格納ディレクトリは自分の環境に合わせます。このでぃれくとりがあることを確認します。

  • Redmineディレクトリ退避
sudo mv redmine /path/to/backup/redmine_$(date +%Y%m%d)

バックアップ環境は自分の環境に合わせます。

  • Redmineディレクトリ退避確認
ls -l /path/to/backup/redmine_$(date +%Y%m%d)

ディレクトリやファイル一式があることを確認します。

  • 退避後ディレクトリ確認
ls -ld redmine

退避させたディレクトリが元のでぃれくとりにないことを確認します。

ソースダウンロード

  • ディレクトリ作成
sudo mkdir /home/www-data/redmine

自分の環境に合わせます。

  • ディレクトリの所有者変更
sudo chown -R www-data:www-data /home/www-data/redmine
  • svnダウンロード
sudo -u www-data svn co https://svn.redmine.org/redmine/branches/5.1-stable /home/www-data/redmine

5.1系の最新安定版をダウンロードします

退避させたディレクトリからconfigファイルコピー

  • 退避させたRedmine→ 新規に作成したRedmineにコンフィグをコピー
sudo cp -pi /path/to/backup/redmine_$(date +%Y%m%d)/config/database.yml /home/www-data/redmine/config/database.yml

コピー元・コピー先は自分の環境に合わせます。

  • コンフィグの中身確認
cat /home/www-data/redmine/config/database.yml

コピーされていることを確認します。

  • 退避させたRedmine→ 新規に作成したRedmineにメール設定情報などをコピー
sudo cp -pi /path/to/backup/redmine_$(date +%Y%m%d)/config/configuration.yml /home/www-data/redmine/config/configuration.yml

コピー元・コピー先は自分の環境に合わせます。

  • 設定情報の中身確認
cat /home/www-data/redmine/config/configuration.yml

Redmineインストール

  • ディレクトリ移動
cd /home/www-data/redmine
  • bundle
sudo -u www-data bundle install --without development test --path vendor/bundle
  • シークレットトークン発行
sudo -u www-data bundle exec rake generate_secret_token
  • DBマイグレーション
sudo -u www-data RAILS_ENV=production bundle exec rake db:migrate
  • 言語設定
sudo -u www-data RAILS_ENV=production REDMINE_LANG=ja bundle exec rake redmine:load_default_data

apache再開

  • apache再開前確認
systemctl status apache2.service

inactive(dead)を確認します

  • apache再開
sudo systemctl start apache2.service
  • apache再開確認
systemctl status apache2.service

active(runnning)を確認します

再作成後の仮パスワード作成

対象のRedmineにアクセスします。

IDとパスワードがadmin / admin に戻っている状態のため、ログイン後、仮パスワードを発行します。

退避したディレクトリからデータを再配置

この状況では単に「再作成されたRedmine」だけができている状況です。ここから、バックアップを流し込んでいきましょう。

  • 退避したRedmineのプラグインディレクトリに移動
cd /hpath/to/backup/redmine_$(date +%Y%m%d)/plugins && pwd
  • プラグイン一式のコピー
sudo cp -pir ./* /home/www-data/redmine/plugins/
  • 退避したRedmineのテーマディレクトリに移動
cd /path/to/backup/redmine_$(date +%Y%m%d)/public/themes
  • テーマ一式のコピー
sudo cp -pir ./* /home/www-data/redmine/public/themes/

いくつかのファイルを上書きするか求められるので、yで返します。

  • 退避したRedmineの添付ファイル格納ディレクトリに移動
cd /path/to/backup/redmine_$(date +%Y%m%d)/files
  • 添付ファイル一式のコピー
sudo cp -pir ./* /home/www-data/redmine/files/
  • 退避したRedmineのログディレクトリに移動
cd /path/to/backup/redmine_$(date +%Y%m%d)/log
  • ログ一式のコピー
sudo cp -pir ./* /home/www-data/redmine/log/

プラグイン再マイグレーション

  • Redmineのルートディレクトリに移動
cd /home/www-data/redmine
  • bundle
sudo -u www-data bundle install
  • プラグインのDBマイグレーション
sudo -u www-data bundle exec rake redmine:plugins:migrate RAILS_ENV=production

apacheリスタート

  • apache再開前確認
systemctl status apache2.service

active(running)を確認します

  • apache再開
sudo systemctl restart apache2.service
  • apache再開確認
systemctl status apache2.service

active(runnning)を確認します

DBリストア

  • mysqldumpを行ったディレクトリに移動
cd /hoge && pwd
  • DBリストア
mysql -h localhost -u redmine -p redmine < redmine_backup.$(date +%Y%m%d).sql

パスワードはredmineインストール時に設定したDBユーザのパスワードです。

apacheリスタート

  • apache再開前確認
systemctl status apache2.service

active(running)を確認します

  • apache再開
sudo systemctl restart apache2.service
  • apache再開確認
systemctl status apache2.service

active(runnning)を確認します

動作確認

この状態でRedmineに管理者権限でログインします。手順通りなら

  • テーマ
  • 添付ファイル
  • プラグイン
  • チケット一覧

などが有効に動いています。

オプション:作業後-退避前のディレクトリ一式を削除-

  • 退避させたディレクトリの直上に移動
cd /path/to/backup

自分の環境に合わせます。

  • 退避ディレクトリ確認
ls -ld redmine_$(date +%Y%m%d)

ディレクトリがあることを確認します。

  • 退避ディレクトリ削除
sudo rm -rf redmine_$(date +%Y%m%d)
  • 退避ディレクトリ確認
ls -ld redmine_$(date +%Y%m%d)

ディレクトリが無いことを確認します。

オプション:作業後-MySQLのダンプファイルを削除-

  • mysqldumpを行ったディレクトリに移動
cd /hoge && pwd
  • ダンプファイル確認
ls -l redmine_backup.$(date +%Y%m%d).sql

ファイルがあることを確認します。

  • ダンプファイル削除
rm redmine_backup.$(date +%Y%m%d).sql
  • ダンプファイル削除確認
ls -l redmine_backup.$(date +%Y%m%d).sql

ファイルが無いことを確認します。

備考

Redmineはバージョンさえ合っていれば

  • DB
  • 設定ファイル
  • 添付ファイル
  • プラグイン
  • テーマ
  • ログ

を移行することで、Redmineの作り直しのみならず、別のサーバへの引っ越しが可能になります。

この、避難訓練さえ行っていけば「何かがあってもバックアップさえあれば動かせる」という心の安定につながるでしょう。

免責事項:これは甲斐谷忍先生の作品『ONE OUTS』に敬意を表したシステム名/ファンアートであり、公式(集英社、製作委員会など)とは一切関係ありません。

『ONE OUTS』システム(Apache/Mod_Security/テキストファイル連携によるWeb防御)解説。 3 OUT

概要

  • Apache
  • Mod_Security
  • テキストファイル

連携によるWeb防御『ONE OUTS』3回目。

ここでは応用編として、筆者の具体的なチューニングや追加設定などをご紹介します。

Cron設定

前項で説明したone_outs.sh。これは、筆者はCron化して自動運用しています。そのため、スクリプトは以下のように簡素化しています。

#!/bin/bash
#
# ONE OUTS System - IP Blacklist Auto-Generator (for cron)
#
# このスクリプトは、Tor出口ノードのリストとApacheのエラーログから
# 不審なIPアドレスを抽出し、ModSecurity用のブラックリストを自動生成します。
# cronなどで定期的に実行することを想定しています。
#

# === 変数の定義 ===

# --- 基本設定 ---
SCRIPT_BASE_DIR="/usr/local/scripts/security"
APACHE_LOG_DIR="/var/log/apache2"
MODSEC_BLACKLIST_FILE="/etc/modsecurity/ip-blacklist.txt"

# --- 除外設定 ---
EXCLUDE_IPS_FILE="${SCRIPT_BASE_DIR}/conf/exclude_ips.txt"

# --- 中間ファイル設定 ---
TOR_EXIT_LIST_RAW="${SCRIPT_BASE_DIR}/work/tor_exit_nodes_raw.txt"
TOR_EXIT_LIST_IPS="${SCRIPT_BASE_DIR}/work/tor_exit_nodes_ips.txt"
SUSPICIOUS_IPS_DAILY="${SCRIPT_BASE_DIR}/work/suspicious_ips_daily.txt"
SUSPICIOUS_IPS_ALL="${SCRIPT_BASE_DIR}/work/suspicious_ips_all.txt"

# === 処理の開始 ===

# 1. Tor出口ノードリストの取得
curl -s -o "${TOR_EXIT_LIST_RAW}" "https://check.torproject.org/exit-addresses"
if [ $? -ne 0 ]; then
    # エラーが発生した場合は syslog に記録
    logger "ONE OUTS Script Error: Failed to download Tor exit node list."
    exit 1
fi
awk '/^ExitAddress/ {print $2}' "${TOR_EXIT_LIST_RAW}" | sort -u > "${TOR_EXIT_LIST_IPS}"

# 2. Apacheエラーログからの不審IP抽出
grep "ModSecurity" "${APACHE_LOG_DIR}/error.log" | \
    grep -o -E "([0-9]{1,3}\.){3}[0-9]{1,3}" | \
    sort -u > "${SUSPICIOUS_IPS_DAILY}"

touch "${SUSPICIOUS_IPS_ALL}"
cat "${SUSPICIOUS_IPS_ALL}" "${SUSPICIOUS_IPS_DAILY}" | sort -u > "${SUSPICIOUS_IPS_ALL}.tmp" && mv "${SUSPICIOUS_IPS_ALL}.tmp" "${SUSPICIOUS_IPS_ALL}"

# 3. ブラックリストの生成
# 変更前のチェックサムを保存
PREV_CHECKSUM=$(md5sum "${MODSEC_BLACKLIST_FILE}" | awk '{print $1}')

# Torリストと不審IPリストを結合して一時ファイルを作成
cat "${TOR_EXIT_LIST_IPS}" "${SUSPICIOUS_IPS_ALL}" | sort -u > "${MODSEC_BLACKLIST_FILE}.tmp"

# 4. 除外IPの削除
if [ -f "${EXCLUDE_IPS_FILE}" ]; then
    grep -v -f "${EXCLUDE_IPS_FILE}" "${MODSEC_BLACKLIST_FILE}.tmp" > "${MODSEC_BLACKLIST_FILE}"
else
    mv "${MODSEC_BLACKLIST_FILE}.tmp" "${MODSEC_BLACKLIST_FILE}"
fi
rm -f "${MODSEC_BLACKLIST_FILE}.tmp" # -f オプションでファイルがなくてもエラーにならないように

# 変更後のチェックサムを取得
POST_CHECKSUM=$(md5sum "${MODSEC_BLACKLIST_FILE}" | awk '{print $1}')

# 5. ファイルに変更があった場合のみApacheをリロード
if [ "${PREV_CHECKSUM}" != "${POST_CHECKSUM}" ]; then
    logger "ONE OUTS Script: Blacklist updated. Reloading Apache."
    systemctl reload apache2.service
fi

exit 0

これをcrontabで設定。(その際にはchmod +x one_outs.shを忘れないように)

# ONE OUTS
0 4 * * * sleep $(shuf -i 0-59 -n 1)m && /home/hoge/script/server/one_outs.sh

として実行します。この、実行分をランダムにするのは、リストが更新されるというトリガーをつかみにくくするためです。

スローロリス攻撃への対処

この、ONE_OUTSの自動実行を行った頃、サーバのレスポンスが悪くなると言う状況を確認しました。以下のようなログを確認(アクセス元などはダミーに置き換えています)

# [クライアントIP] [タイムスタンプ]
# --- 攻撃検知 (1/2): カスタムルールが矛盾したConnectionヘッダーを検知 ---
[Mon Oct 20 11:30:05 2025] [security2:error] [client 198.51.100.201] ModSecurity: Warning. ... [id "10001"] [msg "[CUSTOM RULE] Contradictory Connection header, possible Slowloris probe."] [hostname "your-domain.com"] [uri "/search?query=some-long-query"]

# --- 攻撃検知 (2/2): CRSの標準ルールも同じ異常を検知 (スコア+3) ---
# data "keep-alive, close" の部分で、実際にどのようなデータが送られてきたかを確認できます。
[Mon Oct 20 11:30:05 2025] [security2:error] [client 198.51.100.201] ModSecurity: Warning. ... [id "920210"] [msg "Multiple/Conflicting Connection Header Data Found"] [data "keep-alive, close"] [severity "WARNING"] [hostname "your-domain.com"] [uri "/search?query=some-long-query"]

# --- 最終報告: 合計スコアとブロックに至らなかった状況を記録 ---
# この攻撃単体ではスコアが3のため、ブロックしきい値の5には達していません。
# 他の攻撃と組み合わさった場合に、ブロックの判断材料となります。
[Mon Oct 20 11:30:05 2025] [security2:error] [client 198.51.100.201] ModSecurity: Warning. ... [id "980170"] [msg "Anomaly Scores: (Inbound Scores: blocking=3, detection=3, ... threshold=5)"] [hostname "your-domain.com"] [uri "/search?query=some-long-query"]

これは典型的なスローロリス攻撃。

「Connectionヘッダーにkeep-aliveとcloseを同時に送信する」ことで、コネクションを矛盾させリソースを奪い最終的に枯渇を狙うというのは、攻撃者がCRS(Core Rule Set)に基づいた防御を行っていると確認した際/または単純な威力偵察の際によく使う手です。

漫画『ドリフターズ』に曰く

「こりゃ堕とせんと思ったら
その時から目的は変わるのよ
占領からいやがらせに変わる」

この、いやがらせ目的のため、自分のサーバのリソースが奪われるという状況は見過ごせません。以下のような処置を設けます。

免責事項:これは甲斐谷忍先生の作品『ONE OUTS』に敬意を表したシステム名/ファンアートであり、公式(集英社、製作委員会など)とは一切関係ありません。

『ONE OUTS』システム(Apache/Mod_Security/テキストファイル連携によるWeb防御)解説。 2 OUT

概要

  1. IPアドレスリストによるブロック
  2. エージェントのブロック
  3. ModSecurityのブラックリストのブロック

の3段階のフィルタを設けたONE OUTSシステム。その核となる「ModSecurityのブラックリストの自動生成」です。

スクリプト内容

  • one_outs.sh

こちらを、変数を自分の環境に合わせた状態で 修正していきます。性質上、root権限で作成します。

#!/bin/bash
#
# ONE OUTS System - IP Blacklist Auto-Generator
#
# このスクリプトは、Tor出口ノードのリストとApacheのエラーログから
# 不審なIPアドレスを抽出し、ModSecurity用のブラックリストを自動生成します。
# cronなどで定期的に実行することを想定しています。
#

# === 変数の定義 ===

# --- 基本設定 ---
# スクリプトのベースディレクトリ。環境に合わせて変更してください。
SCRIPT_BASE_DIR="/usr/local/scripts/security"
# Apacheのログディレクトリ。環境に合わせて変更してください。
APACHE_LOG_DIR="/var/log/apache2"
# ModSecurityのブラックリストファイル。SecRuleで指定したパスに合わせます。
MODSEC_BLACKLIST_FILE="/etc/modsecurity/ip-blacklist.txt"

# --- 除外設定 ---
# ブラックリストから除外したいIPアドレスを記述したファイル
# (例: 自分のIPアドレスや、正常なクローラーのIPなど)
EXCLUDE_IPS_FILE="${SCRIPT_BASE_DIR}/conf/exclude_ips.txt"

# --- 中間ファイル設定 (通常は変更不要) ---
# Tor出口ノードの生データをダウンロードする一時ファイル
TOR_EXIT_LIST_RAW="${SCRIPT_BASE_DIR}/work/tor_exit_nodes_raw.txt"
# Tor出口ノードのIPアドレスのみを抽出したリスト
TOR_EXIT_LIST_IPS="${SCRIPT_BASE_DIR}/work/tor_exit_nodes_ips.txt"
# Apacheのエラーログから抽出した不審なIPリスト (日次)
SUSPICIOUS_IPS_DAILY="${SCRIPT_BASE_DIR}/work/suspicious_ips_daily.txt"
# 過去分も含めた、全ての不審なIPリスト
SUSPICIOUS_IPS_ALL="${SCRIPT_BASE_DIR}/work/suspicious_ips_all.txt"
# スクリプト実行時の日付 (YYYYMMDD形式)
TODAY=$(date +%Y%m%d)

# === 処理の開始 ===

echo "--- IP Blacklist Generation Started at $(date) ---"

# 1. Tor出口ノードリストの取得
echo "[Step 1] Downloading Tor exit node list..."
curl -s -o "${TOR_EXIT_LIST_RAW}" "https://check.torproject.org/exit-addresses"

if [ $? -ne 0 ]; then
    echo "Error: Failed to download Tor exit node list."
    exit 1
fi

# ExitAddress行からIPアドレスのみを抽出し、ソートして重複を排除
awk '/^ExitAddress/ {print $2}' "${TOR_EXIT_LIST_RAW}" | sort -u > "${TOR_EXIT_LIST_IPS}"
echo " -> Tor IP list created: ${TOR_EXIT_LIST_IPS}"


# 2. Apacheエラーログからの不審IP抽出
echo "[Step 2] Extracting suspicious IPs from Apache error log..."
# エラーログの中からModSecurityが検知したIPアドレスを抽出
# (grepとsedでIPアドレスのパターンのみを抜き出す)
grep "ModSecurity" "${APACHE_LOG_DIR}/error.log" | \
    grep -o -E "([0-9]{1,3}\.){3}[0-9]{1,3}" | \
    sort -u > "${SUSPICIOUS_IPS_DAILY}"
echo " -> Daily suspicious IP list created: ${SUSPICIOUS_IPS_DAILY}"

# 過去の不審IPリストと今日の日次リストを結合し、最新の完全なリストを作成
# (ファイルが存在しない場合のエラーを避けるため、touchで空ファイルを作成)
touch "${SUSPICIOUS_IPS_ALL}"
cat "${SUSPICIOUS_IPS_ALL}" "${SUSPICIOUS_IPS_DAILY}" | sort -u > "${SUSPICIOUS_IPS_ALL}.tmp" && mv "${SUSPICIOUS_IPS_ALL}.tmp" "${SUSPICIOUS_IPS_ALL}"
echo " -> All suspicious IPs list updated: ${SUSPICIOUS_IPS_ALL}"


# 3. ブラックリストの生成
echo "[Step 3] Generating the final blacklist..."
# TorのIPリストと、これまで蓄積した不審なIPリストを結合
cat "${TOR_EXIT_LIST_IPS}" "${SUSPICIOUS_IPS_ALL}" | sort -u > "${MODSEC_BLACKLIST_FILE}.tmp"


# 4. 除外IPの削除
echo "[Step 4] Removing excluded IPs from the blacklist..."
if [ -f "${EXCLUDE_IPS_FILE}" ]; then
    # grep -v -f を使い、除外リストにあるIPを行ごと削除
    grep -v -f "${EXCLUDE_IPS_FILE}" "${MODSEC_BLACKLIST_FILE}.tmp" > "${MODSEC_BLACKLIST_FILE}"
else
    mv "${MODSEC_BLACKLIST_FILE}.tmp" "${MODSEC_BLACKLIST_FILE}"
fi
rm "${MODSEC_BLACKLIST_FILE}.tmp"
echo " -> Final blacklist created: ${MODSEC_BLACKLIST_FILE}"


# 5. Apacheの再読み込み (設定の反映)
# echo "[Step 5] Reloading Apache to apply changes..."
# systemctl reload apache2.service
# echo " -> Apache reloaded."
# ※ cronで実行する際は、再起動処理が多発しないよう注意が必要です。
#    ファイルに差分があった場合のみ再起動する、などの工夫を推奨します。


echo "--- IP Blacklist Generation Finished at $(date) ---"

作成後、

chmod +x oune_outs.sh

で実行権限を付与します。

そもそも、何故このスクリプトに至ったか?

Torという絶好の隠蔽手段のアクセス者を「バッターボックスに立たせない」

Tor (The Onion Router)はインターネット上での通信を匿名化するための技術とネットワークです。

  • プライバシー保護
  • 検閲回避
  • ジャーナリストや活動家の安全確保

が利用目的。

  1. 入り口ノード(本来のアクセス元からここにアクセス)
  2. 中継サーバ(タマネギの皮を重ねる/剥くように暗号化と複合化で通信の秘匿性を維持)
  3. 出口ノード(アクセス先のサーバにはこの出口ノードからのIPアドレスが表示される)

の三段階で高い匿名性を保っています。ですが、

通信元のIPアドレスが隠されるため、誰がアクセスしているか特定しづらい。

という、サイバー攻撃者にとって非常に都合がいい技術となっています。そのため、この隠蔽手段は最初からアクセスを排除します。

また、このTorの出口ネットワークはほぼ日替わりで更新。

https://check.torproject.org/exit-addresses

この、「攻撃者にとって都合のいい情報」を「防御側も利用できる情報」として転用。

上記スクリプトでは、このURLにアクセスし、出口ノードをダウンロード。その後、IPアドレスの形式で出力します。

攻撃を試みた者は「二度目のチャンスを与えない」

Torを失ってでも不審なアクセスを試みた場合は、Mod_Securityが検知。以下のようなログを検出します。(IPやURLはダミーにしています)

免責事項:これは甲斐谷忍先生の作品『ONE OUTS』に敬意を表したシステム名/ファンアートであり、公式(集英社、製作委員会など)とは一切関係ありません。

『ONE OUTS』システム(Apache/Mod_Security/テキストファイル連携によるWeb防御)解説。 1 OUT

概要

これは、筆者が自分のサーバに組み込んでいるWebセキュリティシステム(と言ってもスクリプトと設定の組み合わせ) 『ONE OUTS』について述べたものです。

  1. OSSで動くこと
  2. シンプルな仕組みであること
  3. メンテナンス性と再現性が高いこと

を目標に構築しました。

メンテナンスが高いとは言え、少々複雑な流れを含むため、いくつかに分けて解説します。

名前の由来

甲斐谷忍先生による同名の野球漫画『ONE OUTS』から来ています。

  • 持ち玉はストレートのみ
  • パワーよりも心理戦で打者を翻弄
  • ルールの裏をかきながらもルールに従う姿勢

などをイメージしながら構築しました。

環境

以下の環境で動いています。

  • Ubuntu 24.04
  • Apache 2.4
    • モジュール
      • mod_rewrite
      • mod_ssl
      • mod_header
      • mod_alias
      • mod_security2
  • シェルスクリプト

次のページから、実際のファイル群を示します。

【改訂・再編集】ApacheにWAF・Mod_Security導入。

こちらの記事の改訂・再編集版です。

そもそもWAFとは?

WAFとはWeb Application Firewallの略で、Webアプリケーション層の脆弱性を狙った攻撃を防ぐためのセキュリティシステムです。

  • UFWやFail2banがIPアドレスやポート番号といった家の玄関を監視するのに対し、WAFは、Webサーバーへ届く手紙(HTTPリクエスト)の中身を解析し、悪意あるスクリプトやコマンドの有無をチェックします。
  • UFWでは、通常のポート(80番や443番)を通る攻撃は防げませんが、WAFは攻撃の内容を解析し、独自のルールセットに基づき「このアクセスは許可するが、このコードがアクセスすることは許可しない」という、より柔軟で厳しいセキュリティチェックを施します。

この機能により、Webサーバーやアプリケーション本体に脆弱性が見つかったとしても、WAFが前段の盾としてこれをカバーできます。

ModSecurityとは?

ModSecurityは、IIS/Apache/Nginxといった主要なWebサーバープログラムにモジュールとして組み込みが可能なオープンソースのWAFです。

  • 導入コスト: ライセンス費用が不要であり、既存のWebサーバーと連携する形で容易に組み込めます。
  • 柔軟性: OSSであるため、高い柔軟性を持ち、設定(チューニング)次第でピンポイントの防御や包括的な防御を併せ持つことができます。

備考(バージョンの選択):

本稿で導入するModSecurityは、Ubuntu 24.04系のパッケージ管理で提供されるEOL (End-of-Life) となっているv2ですが、機能性は単一VPSの防御としては十分です。

v3への移行は、セキュリティ強度とメンテナンス性を考慮し、パッケージ化ないしはOSアップデートなどのタイミングでまた後日検討していきます。

環境

  • Ubuntu 24.04 (22.04でも動作確認)
  • Apache 2.4

※ パッケージ管理にaptitudeを用いています。必要に応じてaptに読み替えてください。

さっくりとした手順

  1. mod_securityのインストールを行います。
  2. mod_securityの設定を行います。
  3. Apacheのバーチャルサイトにmod_securityを組み込みます。
  4. 設定を反映して動作を確認します。

mod_securityのインストールを行います。

  • パッケージ全体のアップデート
sudo aptitude update
  • mod_securityインストール
sudo aptitude install libapache2-mod-security2
  • インストール確認
sudo apache2ctl -M |grep security
security2_module (shared)

と表示されていることを確認します。

ModSecurityの設定

  • 設定ファイル書き換え
sudo mv /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf

推奨ファイルをそのまま設定ファイルとして書き換えます。

OWASP Core Rule Set (CRS)のインストールと設定

  • ディレクトリ移動
cd /usr/share/modsecurity-crs && pwd
  • ルールセットのダウンロード
sudo git clone https://github.com/coreruleset/coreruleset.git
  • ルールセットの設定書き換え
sudo mv /usr/share/modsecurity-crs/coreruleset/crs-setup.conf.example /usr/share/modsecurity-crs/coreruleset/crs-setup.conf

mod_securityモジュールにCRSを読み込む設定を追記

  • ディレクトリ移動
cd /etc/apache2/mods-available/ && pwd
  • ファイルのバックアップ
sudo cp -pi security2.conf /path/to/backup/directory/security2.conf.$(date +%Y%m%d)

任意のバックアップディレクトリを指定します。

  • バックアップ確認
diff -u /path/to/backup/directory/security2.conf.$(date +%Y%m%d) security2.conf

エラーがなければバックアップは成功です。

  • ファイル追記

/etc/apache2/mods-available/security2.confを、以下の差分になるように教義・信仰に沿ったエディタで編集します。(要root権限)

 <IfModule security2_module>
-       # Default Debian dir for modsecurity's persistent data
-       SecDataDir /var/cache/modsecurity
+    # Default Debian dir for modsecurity's persistent data
+    SecDataDir /var/cache/modsecurity

-       # Include all the *.conf files in /etc/modsecurity.
-       # Keeping your local configuration in that directory
-       # will allow for an easy upgrade of THIS file and
-       # make your life easier
-        IncludeOptional /etc/modsecurity/*.conf
+    # Include all the *.conf files in /etc/modsecurity.
+    # Keeping your local configuration in that directory
+    # will allow for an easy upgrade of THIS file and
+    # make your life easier
+    IncludeOptional /etc/modsecurity/*.conf

-       # Include OWASP ModSecurity CRS rules if installed
-       IncludeOptional /usr/share/modsecurity-crs/*.load
+    # --- OWASP Core Rule Set (CRS) の読み込み ---
+
+    # 1. CRSのセットアップファイルを読み込む(必須)
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/crs-setup.conf
+    
+    # 2. CRSのルールファイルを読み込む
+    #    パフォーマンス問題を起こすSQLデータ漏洩検知ルールを除外
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/REQUEST-*.conf
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-950-DATA-LEAKAGES.conf
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-959-BLOCKING-EVALUATION.conf
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-980-CORRELATION.conf
+    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf

→ 修正後のsecurity2.conf全文

<IfModule security2_module>
    # Default Debian dir for modsecurity's persistent data
    SecDataDir /var/cache/modsecurity

    # Include all the *.conf files in /etc/modsecurity.
    # Keeping your local configuration in that directory
    # will allow for an easy upgrade of THIS file and
    # make your life easier
    IncludeOptional /etc/modsecurity/*.conf

    # --- OWASP Core Rule Set (CRS) の読み込み ---

    # 1. CRSのセットアップファイルを読み込む(必須)
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/crs-setup.conf

    # 2. CRSのルールファイルを読み込む
    #    パフォーマンス問題を起こすSQLデータ漏洩検知ルールを除外
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/REQUEST-*.conf
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-950-DATA-LEAKAGES.conf
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-959-BLOCKING-EVALUATION.conf
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-980-CORRELATION.conf
    IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
</IfModule>

※ なぜここまで除外するか?

この、RESPONSE-9x系のルールは、ページの内容に機密情報(クレジットカードのデータなど)が入っていないかを精査します。

これは重要なものですが、昨今のAIボットによる過剰なクロールが挟むと、サーバそのものへの負荷を強め、更にログの圧迫(実際にサーバ容量120GB全てを食い尽くしました)とサーバダウンにつながります。

こちらは個人サイト、単一VPSの運用を旨としているため、ここに関するデータはオミットです。 その分、他の設定の補強でセキュリティ強度を担保します。

  • 設定追記の整合性を確認
sudo apache2ctl configtest

Syntax OKを確認します。

  • Apache再起動
sudo systemctl restart apache2.service
  • Apache再起動確認
systemctl status apache2.service

active (running) を確認します。

Apacheのバーチャルサイト編集

稼働済みのApacheバーチャルサイトの設定ファイルをいじります。バックアップ確認は入念に行ってください。

  • ディレクトリ移動
cd /etc/apache2/sites-available && pwd
  • バーチャルサイトの設定ファイルバックアップ
sudo cp -pi your_site.conf /path/to/backup/directory/your_site.conf.$(date +%Y%m%d)

.confファイルやバックアップディレクトリは自分の環境を指定します。

  • バックアップ確認
diff -u /path/to/backup/directory/your_site.conf.$(date +%Y%m%d) your_site.conf

エラーがなければバックアップは成功です。

  • ファイル追記

/etc/apache2/sites-available/your_site.confを、以下の差分になるように教義・信仰に沿ったエディタで編集します。(要root権限)

# Mod Security

## ModSecurity有効化
SecRuleEngine On
## ModSecurity検知モード
### 検知モードで動かす場合はSecRuleEngine Onをコメントアウトしてこちらを有効化します
#SecRuleEngine DetectionOnly

## ファイルのアップロードをできるようにします。
SecRequestBodyInMemoryLimit 524288000
SecRequestBodyLimit 524288000

## テスト用の検知パラメータを付け加えます。
    SecRule ARGS:modsecparam "@contains test" "id:4321,deny,status:403,msg:'ModSecurity test rule has triggered'"
  • ファイル差分
diff -u /path/to/backup/directory/your_site.conf.$(date +%Y%m%d) your_site.conf
+# Mod Security
+
+## ModSecurity有効化
+SecRuleEngine On
+## ModSecurity検知モード
+### 検知モードで動かす場合はSecRuleEngine Onをコメントアウトしてこちらを有効化します
+#SecRuleEngine DetectionOnly
+ 
+## ファイルのアップロードをできるようにします。
+SecRequestBodyInMemoryLimit 524288000
+SecRequestBodyLimit 524288000
+
+## テスト用の検知パラメータを付け加えます。
+    SecRule ARGS:modsecparam "@contains test" "id:4321,deny,status:403,msg:'ModSecurity test rule has triggered'"
+
  • 設定追記の整合性を確認
sudo apache2ctl configtest

Syntax OKを確認します。

  • Apache再起動
sudo systemctl restart apache2.service
  • Apache再起動確認
systemctl status apache2.service

active (running) を確認します。

mod_security動作確認

  1. ブラウザで、上記の設定を行ったWebサイトにアクセスし、閲覧できることを確認します。
  2. アドレスバーの末尾に?modsecparam=testを追加してアクセスします。

のように、アクセスできないことを確認します。

また、サーバでも

sudo cat /path/to/sites_log/directory/sites_error.log

※ログの格納場所やログの名前は自分の環境に合わせます。

を開き、

ModSecurity: Access denied with code 403 (phase 2). String match "test" at ARGS:modsecparam. [file "/etc/apache2/sites-enabled/your_site.conf"] [line "53"] [id "4321"] [msg "ModSecurity test rule has triggered"] [hostname "host_address"] [uri "/"] [unique_id "xxxxxxx"]

のように、エラーが発生していることを確認します。

備考

WordPress、Redmine等のWebアプリは自身の操作によって「不審なアクセス」として遮断することが極めてよくあります。(偽陽性)

そのため、テストを行った後は

## ModSecurity有効化
#SecRuleEngine On
## ModSecurity検知モード
### 検知モードで動かす場合はSecRuleEngine Onをコメントアウトしてこちらを有効化します
SecRuleEngine DetectionOnly

として検知モードとして動かした方が良いでしょう。

「インターネット接続をする際のApache設定」の常時SSL化とログ設定の手順(長文)

なぜ2020年代から(或いはその5年前から)常時SSL化が必須と言えるのか?

セキュリティの確保

  • 通信の暗号化
    • SSL/TLSの技術により、Webブラウザ~サーバ間の通信が暗号化。第三者による盗聴や改ざんを防ぎます。
  • 入力情報の保護
    • ログイン情報、個人情報、問い合わせフォームの内容など、機密性の高い情報のやりとりが安全になります。

ユーザーの信頼性向上

  • ブラウザの警告表示への対処
    • 2020年代より、Chrome/Safari/Edgeと言った主要なブラウザは従来のhttp通信を「保護されていない通信」としてブラウザに表示するようにしました。これは読者、アクセス者に対してユーザの不安を招きます。
  • 攻撃者にとっての絶好の餌を与えない
    • HTTP通信の状態であるというのは、攻撃者にとっての「オイシい」獲物です。ログイン情報やセキュリティの脆弱性などをより好んで狙うという状況を防ぎます。

(筆者には余り関係ないが) SEOへの影響

  • Googleを筆頭とする各種検索エンジンは、2014年以降、SSL対応を検索上位の評価要因に含めています。
  • HTTPS化されたサイトはSEOで有利になります。

Web標準としての定着

  • HTTPS by default機能
    • Chrome/Edge/ChromiumなどはHTTPSを標準接続とする仕様を導入。これにより、HTTPサイトはますます排除される傾向になります。

企業・サービスの信頼性維持

  • SSL化はもはや企業サイトの基本マナーとされています。
    • 未対応のままでは信頼性を損なう可能性があります。

余談 (私怨かつ直接的な動機)未対応企業への溜飲

  • 強烈なパワハラを喰らい去ることになった会社が「辞めてから10年経ってもHTTP通信のまま」というIT企業の存在は、「あんたは未だにHTTPS化をしていないわけ?」という精神的な勝利に繋がっています。
    • 器が小さいとかみみっちいと言われていても、かなり重要な動機です。

ログが重要な理由

トラブルシューティングが容易になる

  • 最も重視する項目です。先般の「記憶に頼るな。ログを信じろ」の全ての根拠です。
    • 特定のサイトでエラーが発生した場合、該当サイトのエラーログだけを確認できるため、原因特定が迅速になります。
  • バーチャルサイトごとにログがないと以下を招きます。
    • ノイズが多いことによる調査効率の低下。
    • 不審なアクセスの迅速な検知の非効率化。

セキュリティサービスとの連携

  • WAF連携
    • 筆者が用いているModSecurityの連携は、ひとえに「この、エラーログ・アクセスログ」を根拠にセキュリティの強化や利便性強化を図っています。
  • 攻撃のトレンドを知る
    • どのような攻撃があるからサーバはパフォーマンスが落ちているのか? を知る上でもこれは重要です。

サイトごとのアクセス解析が可能/容易になる

  • 複数のドメインやサービスを1台のサーバで運用している場合、ログを分けることで書くサイトのアクセス状況を個別に把握できます。
    • 例えば「example.com」と「sample.jp」のPV数や人気ページを比較したいとき、ログが分かれていないというのは分析が困難になります。

セキュリティ監査に対応しやすい

  • サイトごとにログを分けることで、不正アクセスや改竄の痕跡を個別に追跡可能です。
    • 仮に、何らかの法的なインシデントに巻き込まれたとしても、そのログを元に、迅速な対応が可能になります。

読者への回答

「これだけ長いのに何故2つに分けないのか?」は

  • SSL化
  • ログの設定

は、先に示した「加害者にならないための具体的手順」の1つ。「この両者は不可分であり、インターネットで公開する上では必須」だからです。

  • SSL化という2020年代セキュリティの標準
  • ログの可視化というインターネット黎明期から続くトラブルシューティングと解析

は絶対に必要な項目です。次ページから具体的な手順です。

Webサーバーの基盤設定:ディレクトリ変更とVirtualHostの目的(ローカル環境編)

Apacheの初期設定とWebコンテンツのルートディレクトリを変更する理由と、たとえサイトが一つでもVirtualHostを設定する必要性についてのメモです。

大前提「これはローカル環境での手順」です。

なぜなら、これはインターネット上サーバでの絶対的なルールが抜けています。

  1. https通信の対応
  2. ログ設定の未考慮

この2つは別の項でも解説しますが、これらを考慮しないと

  • 「管理が甘いサーバ」として攻撃者の格好の的になる
  • 「トラブル発生時の証跡をつかめない」

の2点が重くのしかかります。

理由1.なぜホームディレクトリ(DocumentRoot)を変更するのか?

標準でapacheを入れると、Webサーバーのコンテンツディレクトリとして使われるのは /var/www です。

しかし、本稿では/home/www-data へとホームディレクトリを変更します。これには、以下の明白な理由があります。

理由詳細
セキュリティ上の分離 (Preference)/var ディレクトリは、ログ(/var/log)や一時ファイル(/var/tmp)など、システムが頻繁に書き換える揮発性のデータを格納するために設計されています。Webコンテンツを /home に置くことで、システムデータとユーザーデータ(コンテンツ)を明確に分離し、万が一のシステム障害や冗長化の際に管理が容易になります。
ユーザープロファイルの整合性 (Practicality)www-data ユーザーは通常シェルログインできませんが、Ruby on RailsやNode.jsなどのモダンなアプリケーションbundle install やパッケージのインストールを行う際や、gitを利用する際に環境変数として自身のホームディレクトリを参照することがあります。これを /home/www-data に設定することで、アプリケーションがスムーズに動作し、予期せぬエラーを防ぐことができます。

理由2. なぜ単一サイトでもVirtualHost(バーチャルサイト)を仕込むのか?

VPS/ローカルサーバでで公開するサイトが一つだけであっても、デフォルトの設定ファイル (000-default.conf など) ではなく、個別のVirtualHost設定ファイルを作成して運用すべきです。

理由詳細
セキュリティ(デフォルト設定の無効化)デフォルト設定 (000-default.conf) は通常、/var/www/html を DocumentRoot としています。これを無効化し、カスタムVirtualHostのみを有効にすることで、意図しないディレクトリが公開されるリスクや、デフォルト設定のバージョン情報などが露出するリスクを防ぎます。
スケーラビリティと将来性将来、サブドメイン(例: blog.example.com)や別のドメインを追加する際に、既存の設定をコピーして修正するだけで済みます。最初からVirtualHostの構造を採用することで、設定の拡張が容易になり、メンテナンス性が向上します。
HTTPS(SSL)対応の必須要件HTTP(ポート80)からHTTPS(ポート443)への強制リダイレクトや、独自のSSL証明書の設定は、VirtualHostブロック (<VirtualHost *:443>) ごとに行うのが一般的です。VirtualHostが基本となることで、この必須のセキュリティ対策をスムーズに組み込めます。

さっくりとした手順

  1. ホームディレクトリの作成と設定
  2. /etc/passwdの書き換え(要注意)
  3. VirutalHostの設定
  4. 設定の有効化と確認

1. 新たなホームディレクトリの作成と設定

VPSのコンテンツ保存場所を /home/www-data に統一します。

  • ディレクトリ作成
sudo mkdir -p /home/www-data
  • ディレクトリの所有者変更
sudo chown -R www-data:www-data /home/www-data
  • 設定確認
ls -ld /home/www-data

 → 所有者がwww-dataになっていることを確認

2. /etc/passwd ファイルの書き換え

www-data ユーザーのホームディレクトリの定義を /var/www から変更します。

** 注意:** /etc/passwd はシステムアカウントを制御する重要ファイルです。必ずバックアップを取り、変更は一行のみであることを確認してください。

  • バックアップ作成
sudo cp -pi /etc/passwd /path/to/backup/directory/passwd.$(date +%Y%m%d)

→ 任意のバックアップディレクトリを指定します。また、$(date +%Y%m%d)を仕込むことで、「いつ」このバックアップを取ったかが明白になります。

  • バックアップ作成確認
sudo diff -u /path/to/backup/directory/passwd.$(date +%Y%m%d) /etc/passwd

→ 面倒でもこの作業は必須です。というのも、差分を取ることで「オリジナルとバックアップの両方が作成されている」というのを、コマンドという第三者の絶対的な目で見ることができるからです。

→ また、先ほどのcpと逆になっていることにも注意してください。これは、変更後に「どの行が惹かれ、どの行が足されたか」を明確化するためです。

  • sedによるファイル書き換え
sudo sed -i 's|/var/www|/home/www-data|' /etc/passwd

→ 特に注意が必要です。これをミスるとサーバ全体のロックアウトなども容易に発生します。

  • 書き換え確認
diff -u /path/to/backup/directory/passwd.$(date +%Y%m%d) /etc/passwd

差分が以下のみであることを確認します。

-www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
+www-data:x:33:33:www-data:/home/www-data:/usr/sbin/nologin

→ ここで、「なぜ、逆にして差分を取るのか」を明確化。

diff -u /etc/passwd /path/to/backup/directory/passwd.$(date +%Y%m%d)

とした場合、得られる情報は「まるで逆」になります。

+www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
-www-data:x:33:33:www-data:/home/www-data:/usr/sbin/nologin

となり、「バックアップの方が正しい修正」となってしまうからです。

3. VirtualHost 設定の準備(Redmineを例にする)

Redmine(動的アプリケーションの例)を /home/www-data 内のディレクトリで公開するためのベース設定を作成します。

cat <<- __EOF__ | sudo tee -a /etc/apache2/sites-available/redmine.conf
<VirtualHost *:80>
    ServerName 【hoge.example.com】
    # ServerNameは自身が設定したドメイン名に置き換えてください。

    DocumentRoot /home/www-data/redmine/public

    <Directory /home/www-data/redmine/public>
        # .htaccessによる設定変更を許可
        AllowOverride All
        # 複数Viewを無効化(アプリケーションのルーティングを優先させるため)
        Options -MultiViews
        # 全てのアクセスを許可
        Require all granted
    </Directory>
</VirtualHost>
__EOF__

4. 設定の有効化と確認

作成したVirtualHostを有効化し、デフォルトサイトを無効化します。

  • 既存のデフォルトサイトを無効化
sudo a2dissite 000-default.conf
  • 新しいサイト設定を有効化
sudo a2ensite redmine.conf
  • 構文チェック
sudo apache2ctl configtest

→ Syntax OKとなることを必ず確認してください。でないと、apacheサービスが停止したままとなってしまい、サービス断が発生します。

  • Apache サービス再起動
sudo systemctl restart apache2.service
  • Apacheサービス再起動確認
systemctl status apache2.service

active(running)を確認します。

これで、Apacheの基盤がセキュリティと運用効率を考慮したカスタム設定で動作するようになりました。

改訂・再編集『Ubuntu24.04にApacheをインストールするための具体的手順』

この記事を今のトレンドに合わせた上でのステップバイステップとした記事です。

概要

Ubuntu24.04にWebサーバーApacheをインストールします。最近のトレンドではNginxではあるものの、

  1. 豊富なモジュールとカスタマイズ
  2. 動的コンテンツの設定をしやすい
  3. 小規模サイトを立ち上げる上での手間の少なさ
  4. 外部ファイルやモジュールの連携により、以下のような細かい設定が可能
  • 自宅等からのアクセスログを残さず、ログの透明化を図る
  • Robots.txtを無視する悪質なクローラーの排除
  • mod_securityに代表されるWAF(Web Application Firewall)の設置

を考慮してのApache設定です。

さっくりとした手順

  1. (未実施の場合必須)UFWの設定を行います。
  2. Apacheのインストールを行います。
  3. Apacheの設定を行います。
  4. 設定の反映を確認します。

(未実施の場合必須)UFWの設定

この作業、サーバ移設などになれている人ほど陥る罠です。「設定はしっかりしている。なのにサンプルページすら引っかからない!」という場合、大概が「UFWでポート80/443を空けていない」パターンが大半を占めます。

大前提

SSH接続を許可(ポート22はSSH記事で許可済みを前提とする)。

設定の前の心構え:

UFWは堅牢であると同時に融通の利かない門番です。設定を間違えると「自分のサーバにログインできない」事態が易々と発生します。

そのため、この作業に臨む際は落ち着いて臨みましょう。コマンドを打つ際に3回ぐらい深呼吸してもいいぐらいの心構えです。

  • http通信を許可する
sudo ufw allow http comment 'Allow HTTP traffic for Apache'
  • https通信を許可する
sudo ufw allow https comment 'Allow HTTPS traffic for Apache'
  • 設定を確認する
 sudo ufw status verbose

上記、http/httpsが有効になっていることを確認します。

  • UFWが有効になっていない場合:有効化
sudo ufw enable 

インストールを行います。

  • パッケージ全体のアップデート
sudo aptitude update 
  • apacheのインストール
sudo aptitude install apache2
  • バージョン確認
apache2ctl -v
  • 表示例
Server version: Apache/2.4.62 (Ubuntu)
Server built:   2024-07-22T12:37:10
  • サービス稼働確認
systemctl status apache2.service

enabledactive (running)を確認します。

設定を行います。

  • 設定ファイルのバックアップ
sudo cp -pi /etc/apache2/apache2.conf /path/to/backup/directory/apache2.conf.$(date +%Y%m%d)

任意のバックアップディレクトリを指定します。

  • 設定ファイルのバックアップ確認
diff -u /path/to/backup/directory/apache2.conf.$(date +%Y%m%d) /etc/apache2/apache2.conf

差分が無いことでバックアップを確認します。

  • 設定ファイル追記
sudo tee /etc/apache2/apache2.conf > /dev/null << 'EOF'
ServerSignature Off
ServerTokens Prod
ServerName example.com  # <-- ここに実際のドメイン名またはホスト名を指定
EOF

自分のサーバー名を英数字で置き換えてください。

  1. サーバーの署名をオフにして
  2. 最小限の情報のみを公開し
  3. Webサーバの名前を指定する

内容です。

  • 追記確認
diff -u /path/to/backup/directory/apache2.conf.$(date +%Y%m%d) /etc/apache2/apache2.conf
  • 差分内容
+ ServerSignature Off
+ ServerTokens Prod
+ ServerName 自分のサーバー名

設定反映を確認します。

  • 構文確認
sudo apache2ctl configtest

Syntax OKを確認します。

  • サービス再起動
sudo systemctl restart apache2.service
  • サービス再起動確認
systemctl status apache2.service

active (running)を確認します。

  • 設定反映確認
curl -I http://localhost

以下のように、ServerヘッダーにApacheのみが表示されていることを確認します。

Server: Apache

単一VPSでインターネット公開のVPSの運用上の注意 または 「加害者にならないための心構え/具体的手順」。

はじめに

前回の続き。Linuxサーバ運用上の注意を述べましたが、更に難易度が上がり

「このLinuxサーバをインターネットで公開するとき」

の心構えについて述べます。

絶対的な違い:「インターネット上サーバの理不尽な非対称性」

そもそも、ローカル環境とインターネットにこれを公開するというのは

  • 技術
  • 倫理観
  • 運用の手間

が何もかも違います。その中で一番気をつけなければならないのは

「自分自身が加害者になってしまうこと」

です。これはもっと具体的に言うと

「管理者権限を乗っ取られ、自分のサーバが攻撃ツールのフロントエンド/バックエンドとして機能すること」

を意味します。攻撃者は自分自身が表立って行動することを嫌います。そのため、

  • 管理の甘いサーバ
  • 脆弱性があるサービス

などにつけ込み侵入を試み、それを自らの配下として利用します。これは、先に述べた

  • システムダウン
  • バックアップ不備による切り戻し不可

とは何もかもレベルが違うリスクです。

ぶっちゃけ、自分自身が運営しているサーバの瓦解は自分が泣けばそれで済む話ではありますが、自分が管理しているサーバが攻撃に関与したというのは、法的/金銭的/社会的な失墜が絡む大問題。

この、理不尽なまでの非対称性が絡むということを最初に意識してインターネット上サーバでの運営を意識しましょう。

趣味/勉強の範疇での最低限の手段

とはいえ、趣味や勉強の範囲でインターネット接続環境でもLinuxサーバを立ち上げたいという方はいるかと思います。

ここからは、メソロドジーから一歩踏み込み、「より具体的な手段」について述べます。

想定する環境

  • とりあえずLinuxサーバをvpsで立ち上げた。
  • 様々な環境から接続をするから確固たる固定IPを持っていない
  • それでもWebサーバを立ち上げたい

ちょっとした茨の道を進みたい方向けです。

※ ここからは筆者の過去記事から「特に必要な設定」を抜き出し、加筆編集を加えた記事です。※

話の前に筆者環境

  • Ubuntu 24.04
  • apt/aptitudeのパッケージ管理システム

を用います。つまり、makeやリビルドなどは発生しない、再現性の高い環境でのステップバイステップです。

メンテナンス用のユーザを作成する

この作業は極めて重要です。というのも、先に示したとおり、攻撃者が真っ先に/そして最終的に狙うのは管理者権限。つまり、(root)です。

rootへのネットワークを介したログインを禁止すると同時に、メンテナンス用のユーザを作成し、NW上からは、ここから接続することを厳命します。

  • (コンソールを利用して)ユーザ作成
adduser [メンテナンス用ユーザ]

[メンテナンス用ユーザ]は英数字です。その後、パスワード設定などを対話式で求められるので、指示に従って入力します。

  • ログイン確認
exit

物理コンソール/VPSのWebシリアルコンソールから抜けます。

メンテナンス用のユーザ作成確認(メンテナンス用のユーザ)

  • 作成したユーザでログイン確認

Webコンソールで、作成したユーザ名とパスワードでログインできることを確認。

whoami

で、設定したアカウントであることを確認します。

  • rootに昇格できないことを確認
sudo su -

としても、rootに昇格することはできません。なので、一度

exit

でコンソールを抜け、rootでログインし直します。

メンテナンス用のユーザのroot昇格設定(root)

  • メンテナンス用のユーザにsudoを追加

rootでログイン後、

usermod -aG sudo [メンテナンス用ユーザ]

で、sudoグループにこのユーザを加えます。

  • sudo追加確認
id -a [メンテナンス用ユーザ]

で、以下のように表示されることを確認します。

uid=1000(hoge) gid=1000(hoge) groups=1000(hoge),27(sudo),100(users)

修正したユーザのグループに、27(sudo)と表示されることがポイントです。

確認後、

exit

で更にrootを抜け、今度はメンテナンス用のユーザでログインします。

rootのロック (メンテナンス用のユーザ)

  • root昇格

メンテナンス用のユーザでログイン後、

sudo su -

で、rootに昇格できることを確認します。

whoami

root表示されることも確認します。

  • rootロック
passwd -l root

として、rootそのものをロックします。

  • rootのロック確認
exit

を2回行い、Webコンソールから抜けます。

rootロック確認

  • メンテナンス用のユーザ→Webコンソールでログイン可能
  • root→Webコンソールでログイン不可能を確認します。

これ以降はメンテナンス用のユーザで作業を行います。

SSH設定

Ubuntu Desktop系と違い、Ubuntu Serverではsshdがデフォルトでインストールされていない場合があります。

sudo apt install ssh

で、sshdをインストールします。

インストール後、

ssh -V

でバージョンが表示されることを確認し、

systemctl status ssh.service

で、runningenabledを確認します。

このSSHは鍵認証でログインできるようにします。この鍵認証のセキュリティ強度はパスワードと段違い。

鍵交換認証にする理由
  • パスワードが送信されない
    • パスワード認証では、パスワード自体がネットワーク上を流れるため盗聴リスクがあります。
    • 鍵認証では、秘密鍵が署名を生成し、署名のみが送信されるため、秘密情報が直接送られることはありません
  • 総当たり攻撃に強い
    • パスワードは文字数が少ないと短時間で破られる可能性があります。
    • 鍵認証では、2048ビット以上の鍵が使われることが多く、現在の一般的なサーバの計算能力では事実上破ることが不可能です。
  • 盗聴されても再利用できない
    • 鍵認証では毎回異なるチャレンジに対して署名を行うため、録音や再送信による攻撃(リプレイ攻撃)が通用しません。
  • フィッシング耐性が高い
    • パスワード認証は偽サイトに入力してしまうリスクがあります。
    • 鍵認証では秘密鍵がローカルに保管されており、外部に送信されないためフィッシングに強いです。

SSH鍵ペア作成

ssh-keygen -t ed25519
  • 鍵の格納場所は空Enter。(/home/hoge/.ssh/
  • パスワードを設定します。

SSH鍵ペア作成確認

  • 鍵格納ディレクトリに移動
cd .ssh
  • ファイルの内容確認
ls -l

以下のファイルを確認します。

  1. id_ed25519
  2. id_ed25519.pub ※これらのファイルはscp等で自分のクライアントにコピーします。

鍵の設定変更

公開鍵をauthorized_keysに変更し、パーミッションを厳密にします。

  • ファイル名変更
cat id_ed25519.pub >> authorized_keys
  • パーミッション設定
chmod 600 authorized_keys

秘密鍵の保存

この秘密鍵(id_ed25519)は、サーバー全体のアクセス権を持つ、言葉通りの意味でのマスターキーです。

この秘密鍵を奪われることは、サーバーの全権限を奪われることと同義です。 そのため、管理は厳密に、そして自分だけがアクセスできる安全な手段(パスワードマネージャーや暗号化されたストレージなど)で、必ずバックアップをこの段階で行ってください。

その後、適切なターミナルクライアントでこの秘密鍵を登録。接続を確認できたという確証が取れて、初めて実機の前から解放されます。

UFWとFail2banの設定

  1. ファイアウォール(ufw)の設定: 不要な通信をすべて拒否し、必要な通信(SSH, HTTP/S)のみを許可する、基本的な防御壁を構築します。
  2. 不正アクセスの自動検知・ブロック(fail2ban): ログファイルを常時監視し、不審なアクセス元を検知すると、ufwと連携してそのIPアドレスからのアクセスを動的に、そして恒久的にブロックします。
    • SSHへのログイン失敗(3回)
    • ufwがブロックした通信(1回)

Fail2banとは?

fail2banは、サーバーのログファイルを監視し、設定されたパターンに一致する不審なアクセス(パスワード総当たり攻撃など)を検知すると、そのアクセス元のIPアドレスを自動的にファイアウォールの拒否リストに追加する、侵入防止ソフトウェアです。
一度設定すれば、アンインストールしない限りは有効化し続けます。

まず、サーバーの玄関となるファイアウォールufwを設定します。

0-1. システム全体のバックアップ(vpsや仮想サーバの場合)

UFWとfail2banは基本的である分、設定に極めて忠実です。そのため、設定をミスして自分自身のアクセスを禁止した場合、そこに入るための手段はなくなります。

そのため、vps/仮想サーバと言った物理的なログイン手段を持たない場合は、スナップショットやディスクイメージ全体のバックアップを行い、「失敗したときのリカバリー」を行えるようにします。

現に、筆者は、これを怠ったばかりにサーバを作り直したことが3回ほどあります。

0-2. 実機でログインできる準備をしておく(物理サーバの場合)

物理サーバで、サーバ室やデータセンターなどの場合は、この設定が終わるまでは実機の前にいるべきです。

1.1 デフォルトのポリシーを設定

最初に「入ってくる通信は原則すべて拒否、出ていく通信はすべて許可」という基本方針を設定します。これが最も安全な基本設定です。

  • 入ってくる通信を全拒否
sudo ufw default deny incoming
  • 出てくる通信を全許可
sudo ufw default allow outgoing
  • ufw.logの有効化
sudo ufw logging on

これを行わないと、後述するfail2banのアクセスがうまくいきません。

1.2 必要な通信を許可する

次に、必要なサービスへの通信を個別に許可します。SSHの接続を許可する前にufwを有効化すると、サーバーから締め出されてしまうので、必ず先に設定してください。

  • SSH接続を許可(ポート22番)
sudo ufw allow ssh comment 'Allow SSH connections'
  • WebサーバーへのHTTP接続を許可(ポート80番)
sudo ufw allow http comment 'Allow HTTP traffic'
  • WebサーバーへのHTTPS接続を許可(ポート443番)
sudo ufw allow https comment 'Allow HTTPS traffic'

commentオプションで、後からルールを見返したときに分かりやすいようにコメントを付与できます。

これはもちろん基本的な構成です。他のポートを開放・閉じる場合は必要に応じて行います。

1.4 UFWを有効化

ルールを設定したら、ufwを有効化します。

sudo ufw enable

実行すると、「この操作は既存のSSH接続を切断する可能性があります」という警告が出ますが、ステップ1.2でSSHを許可していれば問題ありません。yを入力して実行します。

1.5 設定状態の確認

最後に、設定が正しく反映されているか確認します。

sudo ufw status verbose

Status: activeと表示され、許可したルールが一覧に出ていれば成功です。

1.6 ufw有効化確認

SSHターミナルで

telnet localhost 22

と行ってみます。

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

と通信が可能です。逆に、通信を許可されていないポートでは

telnet localhost 23
Trying 127.0.0.1...
telnet: Unable to connect to remote host: 接続を拒否されました

として、接続することはできません。

手順2:Fail2banのインストールと設定

次に、fail2banをインストールし、ufwと連携させる設定を行います。

2.1 Fail2banのインストール

  • パッケージリストを更新
sudo aptitude update
  • fail2banをインストール
sudo aptitude install fail2ban

筆者の好みでaptitudeを用いていますが、aptでも同様です。

2.2 設定ファイルの仕組み(.conf と .local)

Fail2banの設定は、/etc/fail2ban/ディレクトリにあります。重要なルールとして、jail.confのような.confファイルを直接編集してはいけません。

代わりに、jail.localのように.localという拡張子のファイルを作成し、変更したい項目だけを記述します。.localファイルの内容が.confの内容を上書きする仕組みになっており、これにより、将来Fail2banがアップデートされても、自分が行ったカスタム設定が消えてしまうのを防げます。

2.3 jail.localの作成と解説

教義・信仰に沿ったエディタで、/etc/fail2ban/jail.local というファイル名で新規作成し、以下の内容を記述します。

# SSHへの総当たり攻撃を監視・ブロックする設定
[sshd]
enabled   = true               # このsshd用の設定を有効にする
maxretry  = 3                  # 3回試行を失敗したら
bantime   = -1                 # 無期限にBANする
logpath   = /var/log/auth.log  # この認証ログファイルを監視する
ignoreip  = 127.0.0.1/8 ::1 【ここに自分のIPアドレスを追加】
# ignoreipには、絶対にBANしたくない自分のIPアドレスなどをスペース区切りで指定

# UFWがブロックしたログを監視・ブロックする設定
[ufw]
enabled   = true
filter    = ufw-aggressive
action    = iptables-allports
logpath   = /var/log/ufw.log
maxretry  = 1                  # 1回でもUFWの拒否ログに記録されたら
bantime   = -1                 # 無期限にBANする
ignoreip  = 127.0.0.1/8 ::1 【ここに自分のIPアドレスを追加】

2.4 カスタムフィルターの作成(ufw-aggressive)

上記の[ufw]セクションで指定したufw-aggressiveというフィルターは、自分で作成する必要があります。これは、ufw.logから攻撃元IPアドレスを抜き出すためのルールです。

/etc/fail2ban/filter.d/ufw-aggressive.conf というファイル名で新規作成し、以下の内容を記述します。

[Definition]
failregex = [UFW BLOCK].+SRC=<HOST>
ignoreregex =

failregex: この正規表現パターンに一致するログ行を探し、<HOST>の部分を攻撃元IPアドレスとして認識します。

手順3:設定の反映と動作確認

  1. Fail2banサービスを有効化・起動します。
  • fail2banの有効化(サーバ再起動でも自動的に起動するようにする)
sudo systemctl enable fail2ban
  • fail2banの起動
sudo systemctl start fail2ban
  1. サービスの稼働状態を確認します。
sudo systemctl status fail2ban

active (running)と表示されていれば成功です。

  1. ジェイル(監視)の状態を確認します。
sudo fail2ban-client status

Jail list:に、sshdufwが表示されていれば、監視が開始されています。

これで、不審なアクセスは次回以降、有無を言わせずブロックされる設定となりました。
BANされたIPアドレスは sudo fail2ban-client status sshdsudo fail2ban-client status ufw で確認できます。

まとめ「大いなる力には……」

Linuxの学習で実機を触った方(特にRocky/AlmaなどのRHEL系ディストリビューション)で

sudo su -

を実行したとき、以下のような文言を見かけるのではないでしょうか。

あなたはシステム管理者から通常の講習を受けたはずです。
これは通常、以下の3点に要約されます:
    #1) 他人のプライバシーを尊重すること。
    #2) タイプする前に考えること。
    #3) 大いなる力には大いなる責任が伴うこと。

この#3の「with great power there must also come -- great responsibility」は特に重要です。

少なくとも、Linuxサーバのインターネット公開という「1つの世界」を「他の世界と接続する」行為を試みた者は、この言葉を言葉ではなく魂で理解していきましょう。

余談(という名の本題)

なお、これを力説するのは「筆者が加害者になりかけたことがある」からです。上記の設定などを全く理解せず、rootの意味/覚悟を知らぬままLinuxサーバを自宅から運用するということを行ってしまったため……

たちまちのうちに制御を乗っ取られ、「善意の第三者からの通報」により阻止されたという苦い思いがあります。

この経験は15年もの間、Linuxと筆者を遠ざけ、趣味で触ってもローカル環境で運用するというトラウマとなりました。

ようやく、このトラウマから脱却できつつあるので、初心に立ち返る意味でもこの文章を残します。

Page 1 of 32

Powered by WordPress & Theme by Anders Norén