KubernetesにFalcoをインストールしてランタイムセキュリティ監視と異常動作を検知する

Monitoring tutorial - IT technology blog
Monitoring tutorial - IT technology blog

午前2時、アラートで目が覚めた

Kubernetesクラスターは正常に稼働していた。Prometheus + GrafanaはCPU、メモリに異常なし。クラッシュしているPodもない。しかし決済システムの処理が不思議なほど遅かった——通常のメトリクスでは説明できない遅さだった。

40分かけてログを掘り起こした結果、原因が判明した。あるコンテナが30秒ごとに外部インターネットへcurlプロセスを実行していたのだ。アプリケーションのコードではない。設定されたcronジョブでもない。イメージがいつの間にか侵害されていたのだった。

当時、Prometheus + Grafanaは15台のサーバーを監視していた。メトリクスは充実していて、ダッシュボードも綺麗だった。インフラのメトリクス監視としては申し分なかった——しかしsyscallレベルでコンテナの内部で何が起きているかは完全に盲点だった。そのとき、Falcoを導入することを決意した。

Falcoとは何か、そして既存ツールとの違い

FalcoはSysdigが開発し、現在はCNCFのgraduatedプロジェクトだ。その差別化ポイントはブランドではなく、どこを見るかにある。メトリクスやログではなく、Linuxカーネルのsyscallレイヤーを直接監視する。コンテナが行うすべての操作——ファイルのオープン、プロセスの生成、ネットワーク接続——はすべてここを通過する。

Prometheus + Grafanaが「システムはどう動いているか」という問いに答えるとすれば、Falcoは「私のコンテナの中で誰が何をしているか」という問いに答える。

動作の仕組み

FalcoがKernelにフックする方法は2つある:

  • カーネルモジュール:カーネルに直接モジュールをロードする——パフォーマンスは高いが、特別な権限が必要でカーネルバージョンに依存する。
  • eBPFプローブ:eBPF(extended Berkeley Packet Filter)を使用する——より安全で、カーネルモジュール不要。現代のKubernetesに推奨される選択肢。

仕組みはシンプルだ。syscallをリアルタイムで読み取り、YAMLで定義されたルールのセットと照合し、マッチしたらアラートを発する。デフォルトのルールは最も一般的な危険なパターンをカバーしている:コンテナ内のシェル、/etc/shadowの読み取り、機密パスのマウント、特権昇格など。

HelmでKubernetesにFalcoをインストールする

前提条件

  • Kubernetes cluster (1.19+)
  • Helm 3.x
  • cluster-admin権限のあるkubectl

ステップ1:Helmリポジトリを追加してインストール

# Falcoリポジトリを追加
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update

# 専用のネームスペースを作成
kubectl create namespace falco

# eBPFドライバーでFalcoをインストール(推奨)
helm install falco falcosecurity/falco \
  --namespace falco \
  --set driver.kind=ebpf \
  --set falcosidekick.enabled=true \
  --set falcosidekick.webui.enabled=true

フラグfalcosidekick.enabled=trueはFalcosidekickも一緒にインストールする——FalcoのアラートをSlack、Telegram、PagerDuty、Elasticsearchなど50以上のアウトプット先に転送するフォワーダーだ。これを省略すると、手動でログを読む以外にアラートを受け取る手段がなくなる。

ステップ2:Podの稼働確認

kubectl get pods -n falco

# 期待される出力:
# NAME                                   READY   STATUS    RESTARTS
# falco-6xk9p                            2/2     Running   0
# falco-falcosidekick-7d8f9c-abc12       1/1     Running   0
# falco-falcosidekick-ui-5f6b8d-xyz34    1/1     Running   0

FalcoはDaemonSetとして動作する——各ノードに1つのPodが配置され、クラスター全体がカバーされる。

ステップ3:最初のアラートを確認する

# FalcoのリアルタイムログをTail
kubectl logs -n falco -l app.kubernetes.io/name=falco -f

# アラートをトリガーしてみる:実行中のコンテナにexec
kubectl exec -it <pod-name> -- /bin/sh

# Falcoのログに即座に表示される:
# Notice A shell was spawned in a container with an attached terminal
# (user=root shell=sh parent=runc cmdline=sh container_id=abc123...)

コンテナにexecするだけでアラートがトリガーされる。「Terminal shell in container」は最も疑わしい兆候の一つだ——開発者がデバッグのためにやることもあるが、攻撃者も侵入後に同じことをする。

自分の環境に合わせたルールのカスタマイズ

Falcoのデフォルトルールはかなりノイジーだ——チームが時々デバッグのためにコンテナにexecする場合、誤検知が多発する。チューニングが必要だ。

カスタムルールの作成

custom-rules.yamlファイルを作成する:

customRules:
  custom-rules.yaml: |-
    # 'dev'ネームスペースのコンテナへのexecはアラートしない
    - rule: Terminal shell in container
      condition: >
        spawned_process
        and container
        and shell_procs
        and proc.tty != 0
        and not container.image.repository in (trusted_images)
        and not k8s.ns.name in (dev, staging)
      desc: A shell was spawned in a container
      output: >
        Shell spawned in production container
        (user=%user.name container=%container.name image=%container.image.repository
        pod=%k8s.pod.name ns=%k8s.ns.name)
      priority: WARNING
      tags: [shell, mitre_execution]

    # 新しいルール:暗号通貨マイナーを検知
    - rule: Outbound connection to known crypto mining pool
      desc: Detect connection to cryptocurrency mining pools
      condition: >
        outbound
        and fd.sip.name in (mining_pool_domains)
      output: >
        Crypto mining connection detected
        (pod=%k8s.pod.name image=%container.image.repository
        dest=%fd.sip.name:%fd.sport)
      priority: CRITICAL
      tags: [network, cryptomining]
# カスタムルールを適用
helm upgrade falco falcosecurity/falco \
  --namespace falco \
  --set driver.kind=ebpf \
  --set falcosidekick.enabled=true \
  -f custom-rules.yaml

ルールが読み込まれているか確認

# Falcoがルールを正常に読み込んでいるか確認、シンタックスエラーがないこと
kubectl logs -n falco -l app.kubernetes.io/name=falco | grep -E "(rule|error|warn)" | head -20

FalcoをSlackまたはTelegramに接続する

Falcosidekickは50以上のアウトプット先をサポートしている。Slackの設定が最も手軽だ:

helm upgrade falco falcosecurity/falco \
  --namespace falco \
  --set driver.kind=ebpf \
  --set falcosidekick.enabled=true \
  --set falcosidekick.config.slack.webhookurl="https://hooks.slack.com/services/YOUR/WEBHOOK" \
  --set falcosidekick.config.slack.minimumpriority="warning"

これにより、コンテナ内でシェルが起動したとき、誰かが/proc/*/memにアクセスしたとき、あるいは/etc/passwdに書き込みがあったとき——Slackにすぐ通知が来る。アラートにはコンテキストが十分含まれている:Pod名、ネームスペース、イメージ、ユーザー、コマンドライン。

Falcosidekick UIを確認する

# ダッシュボードを確認するためにPort-forward
kubectl port-forward svc/falco-falcosidekick-ui 2802:2802 -n falco

# ブラウザを開く: http://localhost:2802

インシデント調査のときによくこのUIを開く。アラートのタイムライン、priority(Critical/Warning/Notice)・ネームスペース・ルールでのフィルタリング——見れば何が起きているか、いつからかがすぐわかる。

最初から有効にすべきルール

数百のルールが同梱されているが、すべてが同じ重要度というわけではない。本番環境で特に有用だと感じたものを以下に挙げる:

  • Write below etc:/etcに書き込むプロセス——コンテナ内では極めて異常な動作。
  • Read sensitive file untrusted:/etc/shadow、/etc/sudoers、SSH秘密鍵の読み取り。
  • Netcat Remote Code Execution in Container:リバースシェル作成に使われるnetcat/ncat。
  • Contact K8S API Server From Container:コンテナがKubernetes APIを直接呼び出す——コンテナエスケープの可能性がある。
  • Privilege Escalation Using Sudo:コンテナ内のsudoは明らかなレッドフラグ。

まとめ

あの午前2時の事件の後、翌週にはFalcoをクラスター全体にデプロイした。それ以降、さらに2件の問題を検知した。1件はdeserializationの脆弱性を通じてリバースシェルをインジェクトされたコンテナ、もう1件は開発者がうっかりイメージにクレデンシャルをハードコードしてしまいscript kiddiesにスキャンされたケースだ。

FalcoはPrometheusやGrafanaに取って代わるものではない——メトリクスベースの監視では見えない盲点を埋めるものだ。メトリクスはシステムが遅いと教えてくれる。Falcoはなぜ遅いのかを教えてくれる——そしてその答えがパフォーマンス問題ではなく、セキュリティインシデントであることもある。

本番ワークロードを実行しているクラスターにランタイムセキュリティ監視がまだない場合、これはインシデントが起きてからではなく、起きる前に導入すべきものだ。

Share: