統率者デッキ、再修正。

本当は10月に回したかったデッキです。

統率者

  • F《起源の番人、ザーレル/Szarel, Genesis Shepherd(EOC)》

Szarel, Genesis Shepherd / 起源の番人、ザーレル (2)(黒)(赤)(緑)
伝説のクリーチャー — 昆虫(Insect) ドルイド(Druid)
飛行
あなたの墓地にある土地をプレイしてもよい。
あなたのターン中、あなたがトークンでもこれでもないパーマネント1つを生け贄に捧げるたび、これでないクリーチャー最大1体を対象とする。それの上にこれのパワーに等しい個数の+1/+1カウンターを置く。

2/5

クリーチャー

  • 《甦りし悪夢、ブレイズ/Braids, Arisen Nightmare(EOC)》
  • 《ユーミディアンの荒地起こし/Eumidian Wastewaker(EOC)》
  • 《エヴェンドの低木刈り/Evendo Brushrazer(EOC)》
  • 《アクームの怒り、モラウグ/Moraug, Fury of Akoum(ZNR)》
  • 《土地守/Groundskeeper(EOC)》
  • 《森を護る者/Sylvan Safekeeper(MH3)》
  • 《事件現場の分析者/Aftermath Analyst(EOC)》
  • 《サテュロスの道探し/Satyr Wayfinder(EOC)》
  • 《春花のドルイド/Springbloom Druid(EOC)》
  • 《機能不全ダニ/Haywire Mite(BLC)》
  • 《不屈の追跡者/Tireless Tracker(EOC)》
      1. 手掛かり
  • 《不屈の補給兵/Tireless Provisioner(BLC)》
      1. 宝物
      1. 食物
  • 《水平線の探検家/Horizon Explorer(EOC)》
    • 1. 着陸船
  • 《進化の証人/Evolution Witness(MH3)》
  • 《ムル・ダヤの巫女/Oracle of Mul Daya(EOC)》
  • 《最強のベイロス/Baloth Prime(EOC)》
    • 2. 4/4 ビースト
  • 《アルゴスの庇護者、ティタニア/Titania, Protector of Argoth(EOC)》
    • 3. 5/3 エレメンタル
  • 《猛り狂うベイロス/Rampaging Baloths(EOC)》
    • 2. 4/4 ビースト
  • 《波乱の悪魔/Mayhem Devil(EOC)》
  • 《ウィンドグレイスの魂/Soul of Windgrace(EOC)》
  • 《ギトラグの怪物/The Gitrog Monster(EOC)》
  • 《クロールの死の僧侶、マジレク/Mazirek, Kraul Death Priest(EOC)》
  • 《フェイに呪われた王、コルヴォルド/Korvold, Fae-Cursed King(EOC)》

インスタント

  • 《羅利骨灰/Tear Asunder(EOC)》
  • 《砕土/Harrow(EOC)》
  • 《コラガンの命令/Kolaghan's Command(DTK)》
  • 《ウィンドグレイスの裁き/Windgrace's Judgment(EOC)》

ソーサリー

  • 《信仰無き物あさり/Faithless Looting(UMA)》
  • 《惑星殲滅/Planetary Annihilation(EOC)》
  • 《冒涜の行動/Blasphemous Act(EOC)》
  • 《害獣の侵入/Pest Infestation(EOC)》
    • 4. 1/1 害獣
  • 《森の占術/Sylvan Scrying(BFZ)》
  • 《探検/Explore(WHO)》
  • 《壌土からの生命/Life from the Loam(UMA)》
  • 《自然の知識/Nature's Lore(EOC)》
  • 《遥か見/Farseek(EOC)》
  • 《耕作/Cultivate(EOC)》
  • 《耕作の閃光/Flare of Cultivation(MH3)》
  • 《形なき始まり/Formless Genesis(EOC)》
    • 5. X/X 多相の戦士
  • 《明日への探索/Search for Tomorrow(TSP)》
  • 《見事な再生/Splendid Reclamation(EOC)》
  • 《スカイシュラウドの要求/Skyshroud Claim(EOC)》
  • 《世界魂の憤怒/Worldsoul's Rage(EOC)》
  • 《大渦の脈動/Maelstrom Pulse(ARB)》
  • 《花崗岩の凝視/Gaze of Granite(EOC)》
  • 《僻境への脱出/Escape to the Wilds(EOC)》
  • 《蟲の収穫/Worm Harvest(C18)》
    • 6. 1/1 ワーム

エンチャント

  • 《発生の器/Vessel of Nascency(SOI)》
  • 《パーフォロスの槌/Hammer of Purphoros(THS)》
    • 7. 3/3 ゴーレム
  • 《鏡割りの寓話/Fable of the Mirror-Breaker(NEO)》
      1. 2/2 ゴブリン・シャーマン

アーティファクト

  • 《探検の地図/Expedition Map(2XM)》
  • 《太陽の指輪/Sol Ring(EOC)》
  • 《秘儀の印鑑/Arcane Signet(EOC)》
  • 《探査幼生艦/Exploration Broodship(EOC)》
  • F《世界播種、ハースハル/Hearthhull, the Worldseed(EOC)》

プレインズウォーカー

  • レンと次元壊し

土地

  • 4《沼/Swamp(EOE)》 フルアート
  • 4《山/Mountain(EOE)》 フルアート
  • 5《森/Forest(EOE)》 フルアート
  • 《土建組一家の監督所/Riveteers Overlook(EOC)》
  • 《硫黄泉/Sulfurous Springs(EOC)》
  • 《燻る湿地/Smoldering Marsh(EOC)》
  • 《ラノワールの荒原/Llanowar Wastes(EOC)》
  • 《カープルーザンの森/Karplusan Forest(EOC)》
  • 《燃えがらの林間地/Cinder Glade(EOC)》
  • 《春色の湿原/Vernal Fen(EOC)》
  • 《黄昏のぬかるみ/Twilight Mire(EOC)》
  • 《ボジューカの沼/Bojuka Bog(EOC)》
  • 《ユーミディアンの孵化場/Eumidian Hatchery(EOC)》
    • 8. 1/1 昆虫
  • 《寓話の小道/Fabled Passage(EOC)》
  • 《統率の塔/Command Tower(EOC)》
  • 《ウルザの物語/Urza's Saga(MH2)》
    • 9. 0/0 構築物
  • 《変容する森林/Shifting Woodland(MH3)》
  • 《モリアの坑道/Mines of Moria(LTR)》
  • 《演劇の舞台/Thespian's Stage(WHO)》
    • 宝物
  • 《暗黒の深部/Dark Depths(UMA)》
    • 20/20 マリット・レイジ
  • 《ダクムーアの回収場/Dakmor Salvage(EOC)》
  • 《見捨てられたぬかるみ、竹沼/Takenuma, Abandoned Mire(NEO)》
  • 《不毛の大地/Wasteland(TMP)》
  • 《魂の洞窟/Cavern of Souls(AVR)》
  • 《虹色の眺望/Prismatic Vista(MH1)》
  • 《樹木茂る山麓/Wooded Foothills(ONS)》
  • 《新緑の地下墓地/Verdant Catacombs(ZEN)》
  • 《血染めのぬかるみ/Bloodstained Mire(ONS)》
  • 《草むした墓/Overgrown Tomb(RAV)》
  • 《血の墓所/Blood Crypt(RTR)》
  • 《踏み鳴らされる地/Stomping Ground(GTC)》
  • 《耐え抜くもの、母聖樹/Boseiju, Who Endures(NEO)》
  • 《ケッシグの狼の地/Kessig Wolf Run(ISD)》

OUT

  1. 《苦い真理/Painful Truths(BFZ)》
  2. 《秋の占い師/Augur of Autumn(MID)》

IN

  1. 《不屈の補給兵/Tireless Provisioner(BLC)》
  2. 《鏡割りの寓話/Fable of the Mirror-Breaker(NEO)》

トークンも用意できないていたらくでした。ただ、回していて楽しいデッキです。

10月の食事ピックアップ。

連休時の話。

お世話になっている肩にご馳走になりました。

伊勢エビ

予想外にも程があった伊勢エビ。過去に数回食べたことがありましたが

この量が出るのは驚きでした。しかし、それ以上に驚きだったのが「これは半身です」と言われたこと。

残りの半身は天ぷら。確かに海老は天ぷらの種としての定番ですが、伊勢エビというのも予想外でした。上品な甘味と歯触り、いずれも素晴らしいものでした。

しかし、話はそれだけでは終わらず。

刺身で出てきた尾頭は具足煮。しかも、前身をこんがりあぶった鬼殻焼きスタイルです。

頭の味噌から出る出汁がたっぷり出ていて、残っていた頭の肉も食べ応え十分。

その他食事

これをその他と言っていいのかは不明ですが:

兜煮。全体は崩れず、味がしっかり染みている当最高の出来。目玉のゼラチン質も最高です。

ご飯は伝統的な塩むすびと

ネギトロ&トロタク。

ここまで贅沢でいいのかという形の、非常に充実した食事でした。

『Elasticsearchを利用しているGrowiでのアップデート注意点(Growi特有のプラグインに起因するサービス停止)』

概要

GrowiでElasticsearchによる全文検索を利用している中でのアップデートの注意点です。なお、Growiをオンプレ運用していてサーバリソースの関係上などでElasticsearchを用いていないという方はここから下は参考程度にとどめてください。

環境

  • Grwoi v7.3.3
  • Elasticsearch v9.2.0
  • Apacheによるリバースプロキシ

パッケージアップデート時の注意

sudo aptitude upgrade

(※筆者は好みでaptitudeを使っています)

とした中で、

以下のパッケージが更新されます:          
  elasticsearch 

と、elasticsearchを含むアップデートがあったときの注意点です。

注意1:設定ファイルの維持

(データベースを読み込んでいます ... 現在 170063 個のファイルとディレクトリがインストールされています。)
.../elasticsearch_9.2.0_amd64.deb を展開する準備をしています ...
elasticsearch (9.2.0) で (9.1.5 に) 上書き展開しています ...
elasticsearch (9.2.0) を設定しています ...

設定ファイル '/etc/elasticsearch/jvm.options'
 ==> これはインストールしてから (あなたかスクリプトによって) 変更されています。
 ==> パッケージ配布元が更新版を提供しています。
   どうしますか? 以下の選択肢があります:
    Y か I  : パッケージメンテナのバージョンをインストールする
    N か O  : 現在インストールされている自分のバージョンを残す
      D     : 両バージョンの差異を表示する
      Z     : 状況を調査するためにシェルを開始する
 デフォルトでは現在使っている自分のバージョンを残します。
*** jvm.options (Y/I/N/O/D/Z) [デフォルト=N] ? 

の質問が来た場合、N を確実に選んでください。手なりでのyは厳禁です。なぜなら、Growi設定時に

-Xms256m
-Xmx256m

の2行を追記しているからです。(※数値は環境によって異なりますが、Growi推奨値が256MBです)

この -Xms256m および -Xmx256m の2行は、Elasticsearch の JVMヒープメモリの初期サイズ(Xms)と最大サイズ(Xmx) を指定しています。これが削除される、つまりデフォルト設定に戻ると、Growi の安定性やパフォーマンスに以下のリスクがあります。

削除された場合のリスク

  1. メモリ不足によるクラッシュ
  • デフォルトではJVMが自動でヒープサイズを決定しますが、サーバのメモリ量や他プロセスの使用状況によっては、必要なメモリが確保されず、ElasticsearchがOutOfMemoryErrorで落ちる可能性があります。
  1. パフォーマンスの低下
  • 初期サイズが小さいと、ヒープの拡張が頻繁に発生し、GC(ガベージコレクション)による停止時間が増加します。これにより、Growiの検索やデータ取得が遅くなることがあります。

間違えてY/Iを選択してしまったら?

/etc/elasticsearch/jvm.options.dpkg-old

または

/etc/elasticsearch/jvm.options.dpkg-dist

にオリジナルが残されています。

sudo cp -pi /etc/elasticsearch/jvm.options.dpkg-old /etc/elasticsearch/jvm.options

または

sudo cp -pi /etc/elasticsearch/jvm.options.dpkg-dist /etc/elasticsearch/jvm.options

として復旧させましょう。

注意2:Growi依存プラグインによるサービス起動不可

そうして、アップデートが完了後、

Restarting services...
 systemctl restart elasticsearch.service
Warning: The unit file, source configuration file or drop-ins of elasticsearch.service changed on disk. Run 'systemctl daemon-reload' to reload units.


Job for elasticsearch.service failed because the control process exited with error code.
See "systemctl status elasticsearch.service" and "journalctl -xeu elasticsearch.service" for details.


systemctl status elasticsearch.service
failed (Result: exit-code)

と、failed (Result: exit-code)が出てサービスの再起動に失敗することがあります。この場合、Growiの全文検索は使うことができません。

原因:Growi固有のプラグイン

Growiインストール時、Elasticsearch用の

  • analysis-icu
  • analysis-kuromoji

のプラグインをインストールしています。これはパッケージと同時にアップグレードされません。Elasticsearchのコマンドで導入したパッケージのためです。

対処:再インストール

  • パッケージアンインストール
sudo /usr/share/elasticsearch/bin/elasticsearch-plugin remove analysis-icu
sudo /usr/share/elasticsearch/bin/elasticsearch-plugin remove analysis-kuromoji
  • パッケージ再インストール
sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install analysis-icu
sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install analysis-kuromoji

2つの注意点を対処後

まず、Run 'systemctl daemon-reload'と言われているので、

sudo systemctl daemon-reload

で更新されたデーモン設定ファイルを読み込みます。

次いで、

systemctl status elasticsearch.service

で、Elasticsearchが依然としてfailed (Result: exit-code)となっていることを確認。その後、

sudo systemctl restart elasticsearch.service

としてサービスを再起動。今度はエラーが起きないと思います。

systemctl status elasticsearch.service

でもactive/runningを確認しましょう。

Growi上での最終確認

  1. Growiに管理者権限でアクセスします。
  2. 設定>全文検索管理に進みます。

接続の状態 接続されていますと表示されていることを確認し、

トップページに戻って検索を開始。想定通りの検索結果が出れば対処完了です。

攻撃者の「影響度」を確認する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
  • シェルスクリプト

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

Growi アップデートのエラーに対応。(node_module再インストール)

概要

Growiをアップデートをする中でエラーが発生したので対処した時のメモです。

環境

  • Growi v7.3.2 → v7.3.3にアップデートする際の出来事
    • こちらの手順でv7.3.xにアップデート済み
    • Growiの実行ユーザはroot
  • node v20.19.2
  • npm 11.4.0
  • Apacheによるリバースプロキシ

どの段階でエラーが発生したか

sudo git checkoutsudo pnpm isudo npm run app:buildを実行時に

Tasks:    0 successful, 7 total
Cached:    0 cached, 7 tota
  Time:    25.08s 
Failed:    @growi/pdf-converter#gen:swagger-spec
 ERROR  run failed: command  exited (1) 

が出たのでappのビルドが通りませんでした。

エラーの原因

@growi/pdf-converterが利用する@tsed/openapi-utilsというパッケージが、@tsed/diという別のパッケージからcontextという機能をインポートしようとしていますが、インストールされている @tsed/di のバージョンにその機能が存在しないため、SyntaxError となっています。

これは、pnpm のキャッシュや node_modules ディレクトリの状態が不整合になっている場合に発生するということを突き止めました。

もっと有り体に言うと、筆者環境はGrowiはv7.2.x→v7.3.xと順調にアップデートを重ねていったので、パッケージの混在が発生。言うなれば、複数の設計図が混じっていたので(依存関係の不適合)、建設を担当するnpmが「設計図と実装が矛盾している」としてエラーを吐いた次第です。

大前提

Growiプロセス停止確認(systemdに登録している場合)

systemctl status growi.service

で、growiが完全に停止していることを確認してください。

さっくりとした対処

  1. 依存関係をクリーンにします。
  2. 混在しているパッケージを退避します。
  3. パッケージを再インストールします。
  4. Growiのビルドを改めて実行します。

依存関係をクリアする

  • Growiルートディレクトリに移動&確認 (特に重要な作業)

※調査中に別のディレクトリに遷移しているということは極めて多く発生します。特にエラーの対処となればなおさらです。改めて、作業の現在地を確認します。

cd /growi/root/directroy && pwd

自分の環境に合わせます。(筆者環境/home/www-data/growi)

  • キャッシュクリア
sudo pnpm store prune

node_modulesディレクトリの退避

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

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

sudo mv node_modules /path/to/backup/directory/node_modules.$(date +%Y%m%d)

任意のバックアップディレクトリを退避。$(date +%Y%m%d)変数をつけることで現在の日付を付与します。

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

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

  • 退避ディレクトリ確認
ls -ld /path/to/backup/directory/node_modules.$(date +%Y%m%d)

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

node_modules再配置

  • 再配置
sudo pnpm install

再びnode_modulesが配置されます。

  • 再配置確認
ls -ld node_modules

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

  • ビルド
sudo npm run app:build

Growiの再開

  • Growiサービス開始
sudo systemctl restart growi.service
  • サービス開始確認
systemctl status growi.service

active(running)を確認します。サービスが完全に上がっていないとブラウザでGrowiにアクセスしても503エラーが出ます。しばらく待ちましょう。

アップデート確認

Growiが最新バージョンにアップデートされていれば対処完了です。

【改訂・再編集】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

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

Page 7 of 277

Powered by WordPress & Theme by Anders Norén