FalcoとeBPFによるKubernetesランタイムセキュリティ:実践的な脅威検知ガイド

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

Kubernetesにおけるランタイムセキュリティアプローチの比較

CI/CDにおける静的なイメージスキャンは、デプロイ前に既知のCVEを検出できますが、コンテナが起動した瞬間に監視の目が届かなくなります。攻撃者がゼロデイ脆弱性を悪用したり、コンテナエスケープを実行したり、実行中のPod内で対話型シェルを起動したりした場合、イメージスキャナーではアラートを発行できません。

Kubernetesの監査ログ(Audit Logs)は、Podの作成やConfigMapの変更といったコントロールプレーンのアクティビティを監視するのに役立ちます。しかし、それらのPod内部で発生するプロセスレベルのアクティビティは見落とされます。不正なプロセスが/etc/shadowを書き換えたり、未知のIPアドレス宛てに外部接続を開始したりしても、監査ログには記録されません。これらの脅威を捉えるには、Linuxカーネル内のシステムコールを直接トレースする必要があります。

Falcoはまさにこの境界で動作します。各ノード上でexecve、openat、connectなどの主要なシステムコールをリアルタイムでインターセプトします。Falcoはこれらの未加工イベントを行動ルールと照合・評価し、実行中のKubernetesメタデータ(namespace、Pod名、イメージタグなど)を付与した上で、ミリ秒単位でアラートを生成します。

カーネルモジュール vs モダンeBPFドライバー

Falcoは主に2つのエンジンドライバー(従来のロード可能なカーネルモジュールとモダンなeBPFプローブ)を介してシステムコールをキャプチャします。どちらを選択するかは、クラスターの安定性、ノードのメンテナンス性、そしてクラウドのポータビリティに直接影響します。

1. 従来のカーネルモジュール(DKMS)

  • メリット: レガシーなLinuxカーネル(4.14未満)でも動作し、古いディストリビューションでもCPUオーバーヘッドを低く抑えられます。
  • デメリット: DKMS経由でドライバーをコンパイルするために、すべてのワーカーノードに対応するカーネルヘッダーが必要です。ドライバー内に単一のバグがあるだけでもカーネルパニックを引き起こし、ノード全体を停止させるリスクがあります。また、EKSやGKE Autopilotなどのマネージド環境では、カスタムカーネルモジュールのロード自体が許可されていません。

2. CO-RE対応のモダンeBPFプローブ(推奨)

  • メリット: Linuxカーネルによって検証されたサンドボックス内のバイトコードを実行するため、カーネルパニックのリスクを排除できます。BPF Type Format(BTF)およびCO-RE(Compile Once – Run Everywhere)のおかげで、ローカルでのコンパイルやカーネルヘッダーのインストールが不要です。クラウドのマネージドノードともシームレスに統合できます。
  • デメリット: BTFが有効化されたLinuxカーネル5.8以降が必要です。急激なI/Oスパイク(毎秒50,000システムコール以上)が発生した場合、リングバッファのメモリ使用量が50〜100MB増加することがあります。

本番環境のアーキテクチャとリソースプロファイル

高密度クラスターでカーネルレベルの監視を実行するには、慎重なオーバーヘッド管理が求められます。毎秒2,000〜5,000のシステムコールを処理するノードにおいて、モダンeBPFドライバーを使用したFalcoは、通常1.5%未満のCPU使用率と150MB〜250MB程度のRAM消費に収まります。

標準的な本番環境の構成では、Falcoをワーカーノード全体にDaemonSetとしてデプロイします。そこからFalcosidekickが優先度に応じてアラートをSlack、PagerDuty、SIEMプラットフォーム、またはPodの自動隔離を行うサーバーレス関数へと転送します。

[ Linux Kernel (eBPF Probe) ]
              │ (システムコール: execve, openat, connect)
              ▼
   [ Falco DaemonSet Pod ]
     - ルールエンジンの評価(レイテンシ10ミリ秒未満)
     - K8sメタデータの付与
              │ (gRPC / HTTP POST)
              ▼
     [ Falcosidekick ]
       ├── Slack / PagerDuty(重要アラート)
       ├── SIEM / Elasticsearch(監査ログのアーカイブ)
       └── Webhook / FaaS(Podの自動隔離)

ステップ別の導入・実装ガイド

ステップ1: ノードのカーネルとBTFサポートの確認

まず、クラスターのノードが互換性のあるLinuxカーネル(5.8以上)を実行し、BTFが有効になっていることを確認します。

# すべてのノードのカーネルバージョンを確認
kubectl get nodes -o wide

# ノードのデバッグセッション経由でBTFサポートを確認
kubectl debug node/<your-node-name> -it --image=busybox -- ls -lh /sys/kernel/btf/vmlinux

/sys/kernel/btf/vmlinuxが存在する場合(通常約4〜6MB)、ノード上でのヘッダーパッケージの導入やドライバーのコンパイルを行うことなく、そのままモダンeBPFを利用できます。

ステップ2: Helmを使用したモダンeBPF版Falcoのデプロイ

公式のFalcoリポジトリを追加し、専用のNamespaceを作成します。

helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update
kubectl create namespace falco

falco-values.yamlという名前のカスタムvaluesファイルを作成します。これによりモダンeBPFドライバーを選択し、アラートルーティング用のFalcosidekickを設定します。

driver:
  kind: modern_ebpf

falco:
  json_output: true
  log_stderr: true
  log_level: notice
  priority: notice

falcosidekick:
  enabled: true
  webui:
    enabled: true
  config:
    slack:
      webhookurl: "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"
      minimumpriority: "warning"
      messageformat: "*Falcoアラート:* {{ .Rule }} | Pod: {{ .OutputFields.k8s.pod.name }} | 優先度: {{ .Priority }}"

クラスターにChartをデプロイします。

helm install falco falcosecurity/falco \
  --namespace falco \
  -f falco-values.yaml

DaemonSetのPodとFalcosidekickサービスが正常に稼働していることを確認します。

kubectl get pods -n falco -o wide

ステップ3: カスタム脅威検知ルールの追加

Falcoには堅牢なデフォルトルールが用意されていますが、本番環境のクラスターでは不審なコンテナの振る舞いに対する明示的なポリシーが必要です。不正なシェルの起動や機密認証情報の読み取りを検知するため、以下のルールをcustom-rules.yamlに保存します。

customRules:
  custom-threat-rules.yaml: |-
    - rule: Unauthorized Shell Spawned in Production Container
      desc: 本番環境または決済系Pod内での対話型シェルの実行を検知
      condition: >
        container.id != host and
        k8s.ns.name in ("production", "payment") and
        evt.type = execve and
        proc.name in (bash, sh, zsh, ksh, csh)
      output: >
        本番環境のPodでシェルが起動されました (user=%user.name pod=%k8s.pod.name 
        ns=%k8s.ns.name container=%k8s.container.name image=%container.image.repository 
        cmd=%proc.cmdline)
      priority: CRITICAL
      tags: [mitre_execution, container, production]

    - rule: Sensitive Credentials Read Attempt
      desc: 機密認証情報ファイルへの不正アクセスの試行を検知
      condition: >
        container.id != host and
        fd.name startswith "/etc/" and
        fd.name in ("/etc/shadow", "/etc/sudoers", "/etc/pam.conf") and
        evt.type in (open, openat, openat2)
      output: >
        機密認証情報ファイルへアクセスが発生しました (user=%user.name file=%fd.name 
        pod=%k8s.pod.name ns=%k8s.ns.name cmd=%proc.cmdline)
      priority: WARNING
      tags: [mitre_credential_access, security]

Helmデプロイを更新して新しいルールを適用します。

helm upgrade falco falcosecurity/falco \
  --namespace falco \
  -f falco-values.yaml \
  -f custom-rules.yaml

ステップ4: 検知のトリガーとアラートの検証

production Namespaceに軽量なテスト用Podを起動して、設定を検証します。

# 一時的なテスト用Podを起動
kubectl run test-app --namespace production --image=alpine:3.19 -- sleep 3600

# ルール1をトリガー: 対話型シェルを起動
kubectl exec -it test-app -n production -- /bin/sh

# ルール2をトリガー: シェル内で/etc/shadowの読み取りを試行
cat /etc/shadow

# セッションを終了
exit

Falcoのログストリームを確認し、検知がトリガーされたことを確認します。

kubectl logs -n falco -l app.kubernetes.io/name=falco -c falco --tail=50 | grep "Shell spawned"

Falcoは、実行されたコマンド、コンテナイメージ、Pod名、発生元のユーザーを正確に示した構造化JSONイベントを即座に出力し、インシデント対応チームに必要なコンテキストを迅速に提供します。

Share: