ククログ

株式会社クリアコード > ククログ > ただのログ収集じゃない!クリアコード年表アプリを支えるFluentd + eBPF活用術 #cc20th

ただのログ収集じゃない!クリアコード年表アプリを支えるFluentd + eBPF活用術 #cc20th

こんにちは、Fluentdチームの藤田です。

先日、アナウンスがあった通り クリアコード20周年記念MeatUpに向けた「クリアコード年表」システムを公開しました。

https://20th.clear-code.com/

さて、今回の記事では少し趣向を変えて、この年表アプリの裏側で稼働しているインフラと監視の仕組みについて紹介したいと思います!

クリアコードといえば、データ収集ツールである Fluentd の開発・サポートに深く関わっています。 今回のアプリ運用でも当然 Fluentd を活用しているのですが、ただ単に「Webサーバーのログをファイルに保存する」ためだけに使っているわけではありません。

今回は、Fluentdを「イベントルーター」として活用し、社内チャットへのリアルタイム通知システムを構築した2つの事例をご紹介します。

事例1:アクセスログを「イベント通知」に変換する

年表アプリを公開したからには、ユーザーから新しいエピソードやコメントが投稿されたら、すぐに社内で気づいてワイワイ盛り上がりたいですよね。

これを実現するために、アプリケーション側にわざわざ通知用のコードをゴリゴリ書くのは少し手間で、「リトライ処理はどうしようか?」などなどエラー処理は気が滅入るところです。 そこで、Webサーバーのアクセスログに目をつけました。

「コメントの投稿」は、HTTPリクエストとしては POST /events/xx/comments のようなアクセスとして記録されます。Fluentdのストリーム処理を活用し、この特定のアクセスログだけをリアルタイムにフィルタリングして、社内チャットへ通知として横流しする仕組みを構築しました。

<source>
  @type tail
  path /var/log/nginx/access.log
  pos_file /var/log/fluent/nginx/access.log.pos
  tag nginx.access
  <parse>
    @type nginx
  </parse>
</source>

<filter nginx.access>
  @type grep
  <regexp>
    key method
    pattern ^POST$
  </regexp>
  <regexp>
    key path
    pattern ^/(api/v1/)?events/[0-9]+/comments$
  </regexp>
  <regexp>
    key code
    pattern ^200$
  </regexp>
</filter>

<filter nginx.access>
  @type record_transformer
  enable_ruby true
  <record>
    text "🔔 イベント ${record['path'][%r{/events/([0-9]+)/comments}, 1]} にコメントの投稿がありました!\nhttps://20th.clear-code.com/timelines#event_${record['path'][%r{/events/([0-9]+)/comments}, 1]}_comments"
  </record>
</filter>

<match nginx.access>
  @type zulip
  site https://zulip.clear-code.com
  bot_email_address fujita-bot@zulip.clear-code.com
  bot_api_key <YOUR_API_KEY>

  message_type stream
  stream_name クリアコード
  subject 20周年記念イベント
  content_key text
</match>

record_transformer とRubyの正規表現を組み合わせて、ログの path からイベントIDを抽出し、タイムラインの該当箇所へ直接飛べるURLを動的に生成しているのがちょっとした工夫ポイントです。 これにより、アプリのソースコードを汚すことなく、インフラ側の設定だけでスマートな通知を実現できました。

事例2:カーネルレベルで監視!eBPFプラグインによる特権コマンド検知

ここからが本題です。 Webアプリの運用において、セキュリティ監査やインフラ変更の履歴を追うために「誰がいつ sudo を実行したか」を把握することは非常に重要です。

通常であれば /var/log/auth.log などをFluentdで監視するのがセオリーですが、今回はより堅牢でモダンなアプローチとして、eBPF(Extended Berkeley Packet Filter)を活用しました。

なぜ eBPF を使うのか?

ファイルベースのログ監視には、ログが出力されるまでのタイムラグや、悪意のあるユーザーによってログファイル自体が改ざん・削除されるリスクが伴います。

一方、eBPFはLinuxカーネルの内部に直接フックしてプログラムを安全に実行できる技術です。プロセスが sudo を呼び出した瞬間にカーネルレベルで検知できるため、ログファイルの改ざんの影響を受けず、極めて低負荷かつ確実にイベントを捕捉できます。

新規でFluentdプラグインを作りました

このeBPFの強力な検知能力をFluentdのストリームに乗せるため、今回新たにeBPFを使って特定のコマンド実行をフックし、イベントストリームとして出力するカスタムプラグインを作成しました!

実際の設定がこちらです。

<source>
  @type ebpf_process
  tag ebpf.process
</source>

<filter ebpf.process>
  @type grep
  <exclude>
    key command
    pattern /unix_chkpwd$/
  </exclude>

  # 明示的に sudo 付きで実行したコマンドだけ検知する
  <regexp>
    key parent_comm
    pattern ^sudo$
  </regexp>
</filter>

<filter ebpf.process>
  @type record_transformer
  enable_ruby true
  <record>
    text "🚨 **[WARNING] 特権コマンドの実行を検知しました!**\n👤 ユーザー: `${record['user_name']}`\n💻 コマンド: `${record['command']} ${record['args']}`"
  </record>
</filter>

<match ebpf.process>
  # 社内のチャットへの通知設定
</match>

何もフィルターをかけないと、sudo の内部で呼ばれるパスワード確認用プロセス(unix_chkpwd)などもすべて検知されてしまい、通知がノイズだらけになってしまいます。 そこでこの設定では、不要な内部プロセスを除外しつつ、親プロセスが sudo であるものだけをピンポイントで狙い撃ちしています。

この設定により、サーバー上で sudo が実行された瞬間、社内チャットにアラートが飛んでくるようになります。

アラート通知

ログをただ集めるだけでなく、カーネルレベルのイベントをリアルタイムなオブザーバビリティの向上に直結させるという、非常にモダンで強力なアプローチが実現できました。

今回作成したeBPF関連のプラグインは、以下のリポジトリで公開しています。興味のある方はぜひ覗いてみてください!絶賛開発中です!

おわりに

今回はクリアコード年表アプリの裏側で動いている、Fluentdを活用したイベントルーティングとeBPFによるシステム監視の仕組みをご紹介しました。

データの集約場所としてだけでなく、システムと人をつなぐ柔軟なルーターとして機能するFluentdの強みと、eBPFという低レイヤー技術の実用性を感じていただけたなら嬉しいです。

肝心の「クリアコード年表」アプリも絶賛稼働中です!インフラ基盤はバッチリ整えて待ち構えていますので、ぜひ皆さんのクリアコードにまつわるエピソードをじゃんじゃん投稿して、私たちのチャットを鳴らしまくってください!