Kubernetes向けFalcoによるランタイムセキュリティ:リアルタイム異常検知

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

Kubernetes向けFalcoによるランタイムセキュリティ:リアルタイム異常検知

itfromzero.comをご覧のITエンジニアの皆さん、こんにちは!Kubernetesを扱う中で、セキュリティが常に課題であると感じていることでしょう。APIサーバーの保護、RBACの管理、コンテナイメージの安全性確保まで、多くの側面に注意を払う必要があります。しかし、問題が発生するまで見過ごされがちな側面が一つあります。それが**ランタイムセキュリティ**です。

ある時、私たちのチームは新しいアプリケーションをKubernetesにデプロイしました。すべて順調に見えましたが、数日後、そのアプリケーションのPod内で奇妙なプロセスが実行されているという警告を受け取りました。結局、イメージ内の依存関係に脆弱性があり、攻撃者がそれを利用してリモートコマンドを実行していたのです。

幸いにもFalcoがこの異常な振る舞いをタイムリーに「捕捉」し、大きな被害が出る前に対応することができました。それ以来、私は特に動的なK8s環境におけるランタイム監視の重要性を常に認識しています。本記事では、皆さんが自信を持って自身のクラスターを保護できるよう、Falcoの実践的な経験を共有します。

背景とKubernetesにランタイムセキュリティが必要な理由

Kubernetesは優れた柔軟性とスケーラビリティを提供しますが、それには独自のセキュリティ課題が伴います。従来のソリューションは、ファイアウォール、ネットワーク侵入検知(IDS/IPS)、イメージスキャンなどに焦点を当てることがよくあります。対照的に、ランタイムセキュリティは、コンテナとノードの内部で*実際に何が起きているか*、*稼働中に*何が起きているかに焦点を当てます。

このように想像してみてください。ドアはしっかりと施錠し(ファイアウォール)、出入りする人の身元確認も行っています(イメージスキャン)。しかし、一度侵入者(侵害されたコンテナ)が家に入り込んだら、彼らが何をしているかをどうやって知るのでしょうか?まさにその時、ランタイムセキュリティがその効果を発揮します。

一般的なランタイムの脅威には以下が含まれます。

  • Container Escape: 攻撃者がコンテナから脱出し、ホストノードにアクセスする。
  • 悪意のあるコマンド実行: シェルの実行、ファイルシステムの変更、マルウェアのダウンロード。
  • 機密データへのアクセス: 設定ファイル、Secret、またはアプリケーションデータの読み取り。
  • 予期せぬ構成変更: 重要なシステムファイルやネットワーク構成の変更(例:`/etc/hosts`や`/etc/resolv.conf`の変更)。

CNCFのオープンソースプロジェクトであるFalcoは、この問題解決に理想的な選択肢です。ランタイム脅威検知システム(Runtime Threat Detection)として機能します。Falcoは、システムコールなどのシステムレベルのイベントを監視し、定義されたルールセットと照合することで動作します。ルール違反の振る舞いを検知した場合、Falcoは直ちに警告を発します。

KubernetesへのFalcoのインストール

KubernetesにFalcoをインストールする最も推奨される方法はHelmを使用することです。FalcoはDaemonSetとしてデプロイされ、クラスター内のすべてのノードで実行され、包括的な監視を保証します。

ステップ1: FalcoのHelmリポジトリを追加

まず、FalcoのリポジトリをHelmに追加する必要があります。

helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update

ステップ2: Namespaceの作成(任意ですが推奨)

私は通常、セキュリティツール用に個別のNamespaceを作成し、管理しやすくしています。

kubectl create namespace falco

ステップ3: Helmを使用してFalcoをインストール

これで、作成したNamespaceにFalcoをインストールできます。私は通常、最新バージョンを使用し、必要に応じてFalcoが自動的にカーネルモジュールをロードするように設定しています。

helm install falco falcosecurity/falco \n  --namespace falco \n  --set falco.kind=DaemonSet \n  --set falco.loadKernelModule.enabled=true \n  --set falco.loadBPF.enabled=false

少し解説します:

  • falco.kind=DaemonSet: Falcoがすべてのノードで実行されることを保証します。
  • falco.loadKernelModule.enabled=true: Falcoはシステムコールを監視するためにカーネルモジュールをロードしようとします。これは最も効率的な方法です。
  • falco.loadBPF.enabled=false: カーネルモジュールが動作しない場合、FalcoはeBPFを使用できます。私は通常カーネルモジュールを優先しますが、環境がeBPFをより良くサポートしている場合やカーネルモジュールで問題が発生した場合は、eBPFを有効にできます。

ステップ4: インストールステータスの確認

インストール後、FalcoのPodが安定して実行されているか確認してください。

kubectl get pods -n falco

PodがRunning状態になっているのが確認できるでしょう。次に、Falcoポッドのいずれかのログを確認し、正常に起動しエラーがないことを確認します。

kubectl logs -f <falco_pod_name> -n falco

Falcoの詳細設定:Falco Rulesの力

Falcoの心臓部は**Falco Rules**にあります。これらは、どの振る舞いが正常で、どの振る舞いが疑わしいかを定義するルールです。Falcoには非常に優れたデフォルトのルールセットが付属していますが、真に効果を発揮させるには、環境に合わせてカスタマイズする必要があります。

Falco Ruleの構造

Falcoの各ルールはYAMLファイルで定義され、主要なコンポーネントを含みます。

  • rule: ルールの一意の名前。
  • desc: ルールの説明。
  • condition: ルールがいつトリガーされるかを定義するブール式(Falcoの構文を使用)。
  • output: ルールがトリガーされたときに表示されるメッセージ。
  • priority: 警告の優先度(例:Critical, Warning, Notice)。
  • tags: ルールを分類するためのタグ(例:network, file, shell)。

シンプルなルールの例:

- rule: Detect Shell in Container
  desc: A shell was spawned in a container. This could be normal for debugging, but often indicates suspicious activity.
  condition: >
    spawned_process and container and proc.name in (bash, sh, csh, ksh, zsh)
    and not user.name in (root)
    and not proc.pname=containerd
  output: >
    Shell spawned in container (user=%user.name container=%container.name
    container_id=%container.id image=%container.image.repository
    command=%proc.cmdline parent=%proc.pname pid=%proc.pid)
  priority: WARNING
  tags: [shell, container, process]

Falco Rulesのカスタマイズ

私は通常、Falcoが提供するデフォルトのルールファイル(falco_rules.yamlfalco_rules.local.yaml)を直接編集しません。代わりに、独自のfalco_rules.custom.yamlファイルを作成し、それをFalco Podにマウントします。これにより、変更を失うことなくFalcoを簡単にアップグレードできます。

カスタムルールを追加するには、FalcoのHelm構成を更新できます。次のようなvalues.yamlファイルを作成します。

# values.yaml
falco:
  rules:
    customRules:
      my_custom_rules.yaml: |
        # /etcに機密ファイルを書き込む動作を検知するルール
        - rule: Write Sensitive File in etc
          desc: An attempt to write a sensitive file in /etc directory was made. This is highly suspicious.
          condition: >
            write and fd.name startswith "/etc/" and not fd.name in ("/etc/resolv.conf", "/etc/hostname", "/etc/mtab")
            and container
          output: >
            Sensitive file written in /etc (user=%user.name container=%container.name
            file=%fd.name command=%proc.cmdline)
          priority: CRITICAL
          tags: [filesystem, container, security]

        # 予期せぬ'kubectl exec'の使用を検知するルール
        - rule: Kubectl Exec into Container
          desc: A kubectl exec command was detected which might indicate an interactive shell session in a container.
          condition: >
            spawned_process and proc.name=kubectl and proc.args contains "exec"
          output: >
            Kubectl exec detected (user=%user.name command=%proc.cmdline)
          priority: NOTICE
          tags: [kubernetes, shell]

        # 機密ファイルのパーミッション変更を検知するルール
        - rule: Change Sensitive File Permissions
          desc: File permissions changed on a sensitive file. Could indicate an attempt to elevate privileges.
          condition: >
            chmod and fd.name in ("/etc/passwd", "/etc/shadow", "/etc/sudoers")
            and container
          output: >
            Sensitive file permissions changed (user=%user.name container=%container.name
            file=%fd.name mode=%fd.mode command=%proc.cmdline)
          priority: CRITICAL
          tags: [filesystem, permissions, container]

その後、HelmでFalcoの設定を更新します。

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

Falcoには、ルールを簡潔かつ管理しやすくするためのマクロとリストもあります。例えば、シェルのリストを定義して再利用することができます。

出力(Outputs)の設定

デフォルトでは、Falcoは警告をstdout(Podのログ)に出力します。監視をより便利にするために、Falcoが警告をさまざまな場所に送信するように設定できます。

  • ファイル: 警告をログファイルに記録します。
  • Syslog: Syslogサーバーに警告を送信します。
  • HTTP/HTTPS: WebhookとしてSlack、PagerDuty、またはSIEM(Security Information and Event Management)などのサービスに警告を送信します。
  • gRPC: 他のアプリケーションが警告を受け取れるようにAPIを提供します。

出力を設定するには、Helmチャートをインストールまたはアップグレードする際にvalues.yamlファイルを編集します。例えば、Slack Webhookに警告を送信するには、次のようにします。

# values.yamlに追加
falco:
  falco.jsonOutput: true
  falco.jsonOutputKey: output
  falco.outputs:
    stdout: true
    webhook:
      enabled: true
      url: "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"
      insecure: false
      headers:
        Content-Type: "application/json"
      # customHeaders: [] # カスタムヘッダーが必要な場合
      # template: "{"text":"Falco Alert: %output"}" # Slackメッセージのフォーマットをカスタマイs
      # Slack webhookがサポートしている場合、Falcoが直接JSON出力を送信するためのテンプレートをカスタマイズ
      # またはより複雑なフォーマットが必要な場合
      # template: |
      #   {
      #     "text": "Falco Alert: %output",
      #     "attachments": [
      #       {
      #         "color": "danger",
      #         "title": "%rule",
      #         "text": "%output",
      #         "fields": [
      #           {
      #             "title": "Priority",
      #             "value": "%priority",
      #             "short": true
      #           },
      #           {
      #             "title": "Container",
      #             "value": "%container.name",
      #             "short": true
      #           }
      #         ],
      #         "ts": "%evt.time.s"
      #       }
      #     ]
      #   }

その後、helm upgradeを実行して新しい設定を適用します。

テストとモニタリング

インストールが完了したら、期待通りに動作するか確認する必要がありますよね?これが最も興味深い部分だと私は思います。

イベントを作成してFalcoをテストする

ルール違反イベントを作成し、Falcoがどのように反応するかを確認します。Falcoがデフォルトで提供しているDetect Shell in Containerルールをトリガーしてみましょう。

  1. 任意のPodを見つける: クラスターで実行中のPodを選択します。例えば、シンプルなnginx Podです。
  2. kubectl get pods
  3. そのPodにシェルを実行する:
  4. kubectl exec -it <your_pod_name> -- sh

    Podにshがない場合は、bashash、または利用可能なシェルを試してください。

  5. Falcoのログを確認する: 別のターミナルを開き、Falco Podのログを確認します。

    kubectl logs -f <falco_pod_name> -n falco

    以下のような警告が表示されるはずです。

    ... Warning Shell spawned in container (user=root container=nginx-xxxx-xxxx container_id=xxxxxxxx image=nginx command=sh parent=runc pid=xxxx) ...

さらにいくつかの動作を試して、独自のカスタムルールをテストしてください。

  • Podのシェル内で、/etc/にファイルを書き込もうとします。
  • echo "hello" > /etc/my_suspicious_file.txt
  • または、機密ファイルのパーミッションを変更しようとします(もちろん権限がある場合)。
  • chmod 777 /etc/passwd

これらの各アクションは、対応するルールをトリガーし、Falcoはログに記録します。

監視システムおよびSIEMとの統合

本番環境では、Falcoのログを見るだけでは十分ではありません。全体像を把握し、迅速に対応するために、Falcoを監視システムおよびイベント管理システムと統合する必要があります。

  • Prometheus & Grafana: FalcoはメトリクスをPrometheusが収集し、Grafanaで表示できるようにエクスポートできます。これにより、時間経過に伴う警告の数や種類を追跡できます。
  • ELK Stack (Elasticsearch, Logstash, Kibana): FalcoのログをLogstashに送信し、Elasticsearchに保存し、Kibanaで視覚化します。これはログを分析し、脅威を検索するための非常に強力な方法です。
  • 商用SIEM: Splunk、QRadarなどのSIEMソリューションも、SyslogまたはWebhookを介してFalcoからの警告を受け取ることができます。

ベストプラクティスと実践的なヒント

  • False Positivesの削減: これは最大の課題の一つです。誤検知は、真の警告を見落とす原因となる可能性があります。and not条件を追加して、正当な動作を除外することでルールを微調整してください。例えば、特定のアプリケーションプロセスが頻繁に/tmpに書き込む場合、ルールにand not proc.name=my_app_processを追加します。
  • 警告の優先順位付け: ルールに適切な優先度(priority)を設定します。CRITICAL警告は直ちに対応する必要がある一方、NOTICEは後で検討できます。
  • アプリケーションのニーズに応じたルール作成: 各アプリケーションには独自の動作があります。特定のアプリケーションに特有の異常な動作を検知するために、具体的なルールを作成することをお勧めします。
  • 定期的なルールレビュー:

    Share: