Journald Forward Sealing (FSS) でログの改ざんを防ぐ

Security tutorial - IT technology blog
Security tutorial - IT technology blog

午前2時の沈黙:ログが消えるとき

午前2時14分、Nagios の「Critical」アラートが私のスマホを鳴らした。プライマリのデータベースクラスターの CPU 負荷が 98% に張り付いている。私は急いでデスクに向かい、ラップトップを掴んでノードに SSH でログインした。最初に行ったのは、エンジニアとしての標準的な反射動作、つまりジャーナルの確認だ。

journalctl -xe

空だった。コマンドが吐き出したのは、直近2分間の寂しい5つのエントリーだけだった。それ以前の10時間分の履歴――SSH ログイン、sudo の実行、アプリケーションエラー――が、跡形もなく消えていた。まるで、そのサーバーが午前2時12分に産声を上げたかのように見えた。

それは、侵害が進行中であることを悟る、吐き気のするような瞬間だ。攻撃者は素早いだけでなく、証拠を抹消するほど規律が取れていた。私がログインしたときには、すでにシステムジャーナルは一掃されていた。これが標準的なロギングの根本的な欠陥だ。攻撃者が root 権限を取得すれば、履歴は彼らの意のままになる。ログを削除し、タイムスタンプを書き換え、あるいは偽のエントリーを注入して、管理者に存在しない影を追わせることさえできるのだ。

私はその週をスタックの再構築に費やした。そして何より、二度と盲目にならないための対策に時間を割いた。解決策を探す中で辿り着いたのが、systemd-journald に備わっている Forward Sealing (FSS) だった。

現在の設定に潜む脆弱性

最近のほとんどの Linux ディストリビューションは systemd-journald に依存している。これは高速でバイナリデータをうまく扱うが、デフォルトではログは単なるディスク上のファイルに過ぎない。root 権限があれば、単純な rm -rf /var/log/journal/* や、バイナリエディタによるピンポイントの編集で、フォレンジック調査の痕跡を完全に破壊できてしまう。

(ELK スタックなどへの転送による)ログの集約管理は極めて重要だが、万能薬ではない。そこには必ず「ラグ」が存在する。巧妙なスクリプトなら、ロギングデーモンを停止させ、ローカルバッファを消去し、データがネットワークに流れる前に証拠に巨大な空白を作ることができる。私たちに必要なのは、今読んでいるログが、システムが記録したそのものであると証明する手段だ。

Forward Sealing (FSS) の仕組み

Forward Sealing は systemd に直接組み込まれた暗号化機能だ。Forward Secure Pseudo-Random Generator (FSPRG) を利用して、データの完全性を保護する。

システムは暗号キーのチェーンを生成する。デフォルトでは15分ごとに、新しい「シーリングキー(封印鍵)」を作成し、直前のキーを即座に破棄する。各ログエントリーは、その時点のキーを使用して署名される。古いキーはメモリとディスクから消去されるため、午後3時に root 権限を奪取した攻撃者は、午後2時のログを偽造したり修正したりすることができない。特定の署名を再現するためのキーがすでに存在しないからだ。

これは、15分ごとに模様が変わるデジタルな「封蝋(シーリングワックス)」のようなものだ。侵入者が3時間前の封印を破ったとしても、それを付け替えることはできない。その時点の封印スタンプは永遠に失われているからだ。

Forward Sealing (FSS) の設定方法

セキュリティは境界から始まる。このノードを構築した際、初期の root 認証情報には toolcraft.app/ja/tools/security/password-generator のパスワード生成器を使用した。これはブラウザ内で完結するため、データがネットワークを流れることがない。これは優れた第一歩だが、最強のパスワードであってもゼロデイ脆弱性は防げない。そこで FSS が「ブラックボックス・レコーダー」として機能する。

ステップ 1: キーペアの生成

FSS を初期化するには、キーペアを生成する必要がある。これにより、サーバー用の シーリングキー (Sealing Key) と、管理者用の 検証キー (Verification Key) が作成される。以下のコマンドを実行する:

sudo journalctl --setup-keys

次のような出力が表示される:

シーリングキーが生成され、/var/log/journal/... に保存されました。
検証キー(verification key)は次の通りです: 791a2b845c... (64文字の文字列)

シーリングキーは15分間隔で有効です。
自動的にローテーションされます。

重要: この検証キーを必ずコピーすること。安全なウォレットに保存するか、紙に印刷して物理的な金庫に保管してほしい。このキーを紛失すると、ログの検証ができなくなる。逆に攻撃者に渡れば、ログを偽造されてしまう。サーバー上には一切残さないようにしよう。

ステップ 2: シーリング間隔の調整

デフォルトの15分という窓は通常は十分だが、より高い安全性が求められる環境では精度を上げる必要がある。攻撃者が封印の5分後にログを消去した場合、その5分間のデータは失われる。--interval フラグを使ってこの間隔を短縮できる:

sudo journalctl --setup-keys --interval=10s

10秒間隔にすれば驚異的な粒度が得られるが、CPU のオーバーヘッドはわずかに増加する。現代の Xeon や EPYC ハードウェアでは、影響は通常 1% 未満だが、念のため監視しておく価値はある。

ステップ 3: サービスの有効化

キーが配置されると、systemd-journald はログへの署名を開始する。設定を確実に反映させるため、常にサービスを再起動するようにしている。

sudo systemctl restart systemd-journald

ログの検証

侵害の疑いがある場合は、オフラインで保管していた 検証キー を使ってログを検証できる。ここが FSS の真骨頂だ。

journalctl --verify --verify-key=791a2b845c...

システムはすべてのジャーナルファイルをスキャンし、暗号署名をキーと照合する。ログが改ざんされていなければ、成功メッセージが表示される:

PASS: /var/log/journal/...
ジャーナルファイル /var/log/journal/.../system.journal の検証に成功しました。

攻撃者が1行でも削除したり、タイムスタンプを修正したりしていれば、検証は即座に失敗する。そして、チェーンが切断された正確な箇所を特定してくれる。たとえジャーナルファイルごと削除されたとしても、監査中にシーケンス番号の欠落として明白に検知できる。

現場からの教訓

私は本番環境のクラスターで1年以上 FSS を運用してきた。そこで学んだことは以下の通りだ:

  • ローテーションは自動: ジャーナルファイルがサイズ制限(例:500MB)に達してローテーションされる際も、FSS のメタデータはシームレスに引き継がれる。
  • 多層防御: FSS は「ローカル」ディスクを保護するものだ。依然としてリモートの SIEM にログを転送すべきである。FSS は、ネットワーク経由のログが信用できない状況に陥ったときのバックアップとなる。
  • 定期的な監査: 災害が起きてから動くのではなく、毎月の cron ジョブでログの完全性を検証しよう。これにより、ハードウェアの故障によるデータの破損も早期に発見できる。

なぜこれが重要なのか

調査において、「確実性」こそがすべてだ。午前2時に画面を凝視しているとき、目の前のデータが真実かどうかを知る必要がある。FSS がなければ、それは単に「root ユーザーなら誰でも書けるテキストファイル」を見ているに過ぎない。

FSS はエクスプロイト自体を止めることはできない。攻撃者がデータベースをクラッシュさせるのを防ぐこともできない。しかし、彼らが「隠れる」ことは阻止できる。彼らに足跡を残すことを強いるのだ。インシデント対応という一分一秒を争う状況において、その足跡こそが攻撃者を捕らえる唯一の手がかりになることが多い。

もし、まだこの機能を有効にしていないなら、今日設定してほしい。わずか5分の作業で、次にアラートが鳴ったとき、未来の自分は今の自分に感謝することだろう。

Share: