Fedora Serverで構築するsystemd-journal-remote:HTTPS mTLSによるセキュアな集中ログ管理

Fedora tutorial - IT technology blog
Fedora tutorial - IT technology blog

深夜2時、痕跡を残さず消え去ったサーバーの衝撃

午前2時15分、けたたましいPagerDutyのアラート音で叩き起こされました。本番クラスタでCeleryバックグラウンドジョブを80個捌いていたワーカーノードが、突如オフラインに。Pingは返らず、SSHはConnection timed out。慌ててHetznerのWebコンソールを開き、ハードリセットを実行しました。マシンは40秒後に再起動したものの、ダウンの原因は跡形もなく消え失せていました。

開発機で2年間Fedoraを使い続け、最新カーネルと安定したdnfの恩恵を実感していた私は、新規ワーカーノード4台にも迷わずFedora Server 40を選定していました。ところが、原因究明のためにjournalctl -b -1を叩いても、ターミナルには何も表示されません。Kernel Panicが発生し、Btrfsファイルシステムが強制アンマウントされたことで、RAMバッファ上のログがNVMeストレージへフラッシュされる前に電源が落ちてしまったのです。オンコール担当者にとって、深夜のログ消失ほど恐ろしい悪夢はありません。

ローカル保存だけに依存するログ運用の落とし穴

Fedoraのデフォルト設定では、systemd-journaldがカーネル、systemdサービス、Podmanコンテナに至るまですべてのログを一括収集します。このバイナリロギング機構は極めて高速でフィルタリングも容易です。しかし、ログをローカルディスクのみに保管する構成には、致命的な3つのリスクが存在します。

  • Kernel Panic時のデータ喪失: 突然の停電やハードウェアクラッシュ時、RAMバッファに滞留していたログが/var/log/journal/に書き込まれる前に消滅します。
  • root奪取時の証跡隠蔽: 万が一攻撃者にroot権限を奪われた場合、真っ先にジャーナルファイルが完全消去され、侵入痕跡が消されてしまいます。
  • 3ノード以上における運用の煩雑化: 障害調査のたびに4つのSSHセッションを開き、それぞれでjournalctl -u app.serviceを実行するのは非効率であり、ノード間の相関関係も見落としがちです。

代表的なログ集中管理ソリューションの比較

この課題を解決するにあたり、システム管理者の選択肢は主に3つあります。

1. ELKスタック(Elasticsearch、Logstash、Kibana)またはGraylog

エンタープライズ向けの堅牢な標準構成です。しかし、JVMだけで4〜8GBのメモリを平気で消費するなど、膨大なリソースを要求します。3〜8台程度の小規模クラスタ(メモリ2〜4GBのVPS環境)にELKを導入するのは、明らかにオーバースペックと言えます。

2. 従来のrsyslogへの回帰

rsyslogはメモリ消費量が30MB未満と軽量で扱い慣れたツールです。しかし、デフォルトではプレーンテキスト転送のため、stunnel等でラップしない限りセキュリティ上の懸念が残ります。さらに、構造化ログがプレーンテキストに平坦化されてしまうため、_SYSTEMD_UNITや_PID、_COMMといった有用なメタデータフィールドが失われてしまいます。

3. systemd-journal-remoteとsystemd-journal-upload(HTTPS経由)

systemdに標準搭載されている最適なアプローチです。ホストごとのメモリ消費量はわずか15〜25MB程度に収まります。バイナリメタデータを100%保持したまま、x509証明書を用いた双方向認証(mTLS)で暗号化転送を行います。ログはミリ秒単位でリアルタイム転送されるため、ワーカーが突発的にダウンしても、その0.5秒前までのログはすでにログサーバー側へ安全に格納されています。

Fedora Serverにおけるsystemd-journal-remoteのHTTPS構築手順

検証環境の構成(2台構成):

  • ログサーバー(Fedora Server 40): HTTPSポート 19532、IP 192.168.10.50
  • クライアントノード(Fedora Server 40): ログ送信元、IP 192.168.10.51

ステップ1:拡張パッケージのインストール

ログサーバーとクライアントノードの双方で以下を実行します。

sudo dnf install -y systemd-journal-remote openssl

ステップ2:HTTPS用証明書の発行(mTLS)

不正なマシンからの不正ログ送信を防ぐため、双方向認証(mTLS)用のx509証明書を生成します。ログサーバー上で作業を行います。

# 証明書および秘密鍵用ディレクトリの作成
sudo mkdir -p /etc/ssl/journal-remote
cd /etc/ssl/journal-remote

# 1. 内部Root CAの作成(有効期限10年)
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
  -keyout ca.key -out ca.pem -subj "/CN=Journal-Internal-CA"

# 2. ログサーバー用証明書の作成
sudo openssl req -new -nodes -newkey rsa:2048 \
  -keyout server.key -out server.csr -subj "/CN=192.168.10.50"
sudo openssl x509 -req -days 1095 -in server.csr \
  -CA ca.pem -CAkey ca.key -CAcreateserial -out server.pem

# 3. クライアントノード用証明書の作成
sudo openssl req -new -nodes -newkey rsa:2048 \
  -keyout client.key -out client.csr -subj "/CN=192.168.10.51"
sudo openssl x509 -req -days 1095 -in client.csr \
  -CA ca.pem -CAkey ca.key -CAcreateserial -out client.pem

# パーミッション設定:サービス実行ユーザーのみ読み取り可能にする
sudo chown -R systemd-journal-remote:systemd-journal-remote /etc/ssl/journal-remote
sudo chmod 600 /etc/ssl/journal-remote/*.key

scpを用いて、ca.pem、client.pem、client.keyの3ファイルをクライアントノードへ転送します。

scp ca.pem client.pem client.key [email protected]:/tmp/

# クライアントノード側で実行:
sudo mkdir -p /etc/ssl/journal-remote
sudo mv /tmp/{ca.pem,client.pem,client.key} /etc/ssl/journal-remote/
sudo chown -R systemd-journal-upload:systemd-journal-upload /etc/ssl/journal-remote
sudo chmod 600 /etc/ssl/journal-remote/client.key

ステップ3:ログサーバーの設定

ログサーバー側の受信設定ファイルを編集します。

sudo nano /etc/systemd/journal-remote.conf

推奨設定内容:

[Remote]
Seal=false
SplitMode=host
ServerKeyFile=/etc/ssl/journal-remote/server.key
ServerCertificateFile=/etc/ssl/journal-remote/server.pem
TrustedCertificateFile=/etc/ssl/journal-remote/ca.pem

重要な2つのパラメータ:

  • SplitMode=host: /var/log/journal/remote/配下に、ホスト名/IPごとにクライアントのログファイルを自動分割して保存します。
  • TrustedCertificateFile: mTLSを有効化します。内部CAによって署名された正規の証明書を持つクライアントのみログのアップロードを許可します。

firewalldのポート開放とソケットの有効化を行います。

# TCP 19532ポートの開放
sudo firewall-cmd --add-port=19532/tcp --permanent
sudo firewall-cmd --reload

# ソケットの有効化(ログ接続が届くとsystemdが自動でサービスを起動)
sudo systemctl enable --now systemd-journal-remote.socket

ステップ4:クライアントノードのログストリーミング設定

クライアントノード側で/etc/systemd/journal-upload.confを開きます。

sudo nano /etc/systemd/journal-upload.conf

接続先エンドポイントとクライアント証明書を指定します。

[Upload]
URL=https://192.168.10.50:19532
ServerKeyFile=/etc/ssl/journal-remote/client.key
ServerCertificateFile=/etc/ssl/journal-remote/client.pem
TrustedCertificateFile=/etc/ssl/journal-remote/ca.pem

ログ転送サービスを起動します。

sudo systemctl enable --now systemd-journal-upload.service

接続ステータスを確認します。

sudo systemctl status systemd-journal-upload.service

ターミナルにActive: active (running)およびUploaded ... bytesと表示されていれば、暗号化HTTPSチャネルを介したログ転送は正常に確立されています。

ステップ5:リモートログの照会

ログサーバーに戻り、ログ保存ディレクトリを確認します。

ls -lh /var/log/journal/remote/

remote-192.168.10.51.journalというファイルが生成されているはずです。--fileオプションを付けることで、使い慣れたバイナリクエリの速度を維持したままログを検索・抽出できます。

# tail -fのようにリアルタイムでログを追跡
sudo journalctl --file=/var/log/journal/remote/remote-192.168.10.51.journal -f

# 重大なエラー(ErrorからEmergencyまで)のみを直近50件抽出
sudo journalctl --file=/var/log/journal/remote/remote-192.168.10.51.journal -p err..emerg -n 50

本番運用で押さえておくべき実践Tips

軽量で安定性に優れた構成ですが、本番環境では以下の2点に注意が必要です。

1. ディスク容量枯渇(Disk exhaustion)の防止: 多数のノードからログが常時送られてくると、数ヶ月で/varパーティションが逼迫する恐れがあります。/etc/tmpfiles.d/journal-remote.confを作成し、30日以上経過した古いリモートログを自動ローテーション・削除するように設定しておきましょう。

# 30日経過したリモートログをクリーンアップ
d /var/log/journal/remote 0755 systemd-journal-remote systemd-journal-remote 30d

2. 一時的なネットワーク断への対応: systemd-journal-uploadはカーソル状態(cursor state)を保持します。ノード間のネットワークが一時的に切断されても、クライアント側がローカルにログをバッファリングし、接続復旧時に自動で再送するため、ログの欠落は発生しません。

Share: