Techfirm Cloud Architect Blog

テックファーム株式会社クラウドインフラグループのブログ

FalcoでAWS Fargateのランタイムを保護してみる

はじめに

最近コンテナをプロジェクトで使う機会も増えてきましたが、コンテナのセキュリティ対策で悩むことも多いのではないでしょうか。
FalcoがAWS Fargateに対応しているようなので、今回はFalcoでAWS Fargateのコンテナランタイムの保護ができるか検証してみます。

構成

コンテナ定義
Falco

以下はFalcoコンテナのDockerfileです。

FROM scratch

COPY --from=ollypom/pdig:latest /pdig /vendor/falco/bin/pdig
COPY --from=falcosecurity/falco:0.32.2-slim      /usr/bin/falco         /vendor/falco/bin/falco
COPY --from=falcosecurity/kilt-utilities:latest  /kilt/                 /vendor/falco/bin/
COPY --from=falcosecurity/falco:0.32.2-slim      /etc/falco/            /vendor/falco/etc/falco/

VOLUME ["/vendor/falco"]

CMD ["/vendor/falco/bin/waitforever"]

上記定義は、以下を参考にしました。
https://github.com/aws-samples/aws-fargate-falco-examples/tree/main/containerimages/mountedbinaries

PHP

以下はPHPコンテナのDockerfileです。

FROM php:8-fpm
COPY falco_conf/ /data/

「falco_conf」ディレクトリには、falco.yamlfalco_rules.local.yamlを格納します。 アップデートや、PHPの設定などは割愛します。

AWS構成

AWS構成図

タスク定義

今回の構成は、以下のようなタスク定義で実行しました。 全体ではなく、一部抜粋となります。

Falcoはサイドカーでも動作するようですが、AWS Fargateではまだサポートしていないようです。
サンプルにあるmountedbinariesembeddedbinariesの2パターンで動作するようですが、アプリケーションコンテナを汚したくなかったのでmountedbinariesを今回は選択しました。

        {
            "name": "binaries",
            "image": "<ECRのFalcoコンテナURI>",
            "cpu": 0,
            "essential": true
        },
        {
            "name": "php",
            "image": "<ECRのPHPコンテナURI>",
            "cpu": 0,
            "portMappings": [],
            "essential": true,
            "entryPoint": [
                "/vendor/falco/bin/launcher",
                "/vendor/falco/bin/pdig",
                "sh",
                "-c",
                "php-fpm",
                "--"
            ],
            "command": [
                "/vendor/falco/bin/falco",
                "--userspace",
                "-c",
                "/data/falco.yaml"
            ],
            "environment": [
                {
                    "name": "__CW_LOG_GROUP",
                    "value": "/aws/ecs/service/falco_alerts"
                }
            ],
            "volumesFrom": [
                {
                    "sourceContainer": "binaries",
                    "readOnly": true
                }
            ],
            "linuxParameters": {
                "capabilities": {
                    "add": [
                        "SYS_PTRACE"
                    ]
                }
            },
            "dependsOn": [
                {
                    "containerName": "binaries",
                    "condition": "START"
                }
            ],
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "/aws/ecs/service/mountedbinaries",
                    "awslogs-region": "ap-northeast-1",
                    "awslogs-stream-prefix": "falco-app"
                }
            }
        }

entryPointfalcoからphp-fpmを起動したり、linuxParametersSYS_PTRACEを許可しているのがポイントですね。
この辺りの構成は、PrismaCloudとほぼ同じです。

使い勝手

検知

Falcoの検知ルールは、以下ファイルに定義します。

  • /vendor/falco/etc/falco/falco_rules.yaml:デフォルトルール
  • /data/falco_rules.local.yaml:カスタムルール

定義の例は以下のとおりです。
デフォルトルールの例や、GitHubのドキュメントを確認しながらカスタムルールを作っていく感じですね。

- rule: write_binary_dir
  desc: an attempt to write to any file below a set of binary directories
  condition: open_write and not proc.name in (package_mgmt_binaries) and bin_dir
  output: "File below a known binary directory opened for writing (user=%user.name command=%proc.cmdline file=%fd.name)"
  priority: WARNING

- macro: outbound_corp
  condition: >
    (((evt.type = connect and evt.dir=<) or
      (evt.type in (sendto,sendmsg) and evt.dir=< and
       fd.l4proto != tcp and fd.connected=false and fd.name_changed=true)) and
     (fd.typechar = 4 or fd.typechar = 6) and
     (fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8") and
     (evt.rawres >= 0 or evt.res = EINPROGRESS))

- rule: outbound connection
  desc: Container attempted to connect to the outer world
  condition: outbound_corp
  output: "Outbound network traffic connection (srcip=%fd.cip dstip=%fd.sip dstport=%fd.sport proto=%fd.l4proto procname=%proc.name)"
  priority: WARNING
アラート
CloudWatchLogs

始めに、ログをCloudWatchLogsに送信してみます。
Falcoデフォルト値が標準出力に出力されるように設定しているので、ECSタスク定義の設定でCloudWatchLogsに送信されるように設定します。

アラートは、以下のような形式でCloudWatchLogsに出力されました。
エラーの内容は、/etc配下のファイルへの書き込み検知です。

Logsに送信されたFalcoログ

結果としては出力されたのですが、アラートがなかなかCloudWatchLogsに送信されない事象が発生しました。
原因はログはバッファされるようで、いくつかアラートが溜まらないとCloudWatchLogsに送信されないようです。
Fluentd等のログルーターを介して送信するのがよさそうです。

https://falco.org/docs/outputs/channels/

If the logs are inspected by tailing container logs (e.g. kubectl logs -f in Kubernetes) it might look 
like events can take a long time to appear, sometimes longer than 15 minutes. This is not an issue 
with Falco but is simply a side effect of the system output buffering.
Slack

FalcoはSlackにも送ることができるようなのでやってみます。
falco.yamlを以下のように修正します。

#program_output:
#  enabled: true
#  keep_alive: true
#  program: "/vendor/falco/bin/logshipper"
  
json_output: true
program_output:
  enabled: true
  program: "jq '{text: .output}' | curl -d @- -X POST <Slack WebHookURL>"
  • program_outputは1つしか認識しないようなので無効化します。
  • jpコマンドを使うので、コンテナにjpパッケージが必要です。

アラートを起こすと以下のような形式で通知が来ました。Slackの方はすぐにアラートが来ます。
Slackに送信されたFalcoログ

Slackに通知できることが分かりましたが、program_outputが1つしか設定できないのでSlackに送るとCloudWatchLogsに送信できなくなってしまいます。
program_outputでCloudWatchLogsへ送信し、内容を検知してChatBot等でSlackに送るのがよさそうですね。

まとめ

確かにAWS Fargateで動作し、定義したルールに沿って攻撃を検知できることは確認できました。
最近、AWS Fargateにアップデートがありコンテナランタイムセキュリティツールのプロセス監視が容易になったと話題になりましたので、今後のFalcoのアップデートに期待したいです。

参考

falcosecurity/falco
aws-samples/aws-fargate-falco-examples
falco-rules