JMDC TECH BLOG

JMDCのエンジニアブログです

プライベート環境のFargateにサードパーティ製EDRを導入してみた

初めまして。株式会社JMDC データウェアハウス開発部 保険者基盤Gの垂水です。

EDR(Endpoint Detection and Response)というサイバー攻撃対策をFargateへ導入するにあたり、詰まったポイントがあり困ったため手順に解説を入れて紹介します。

尚、情報を隠している箇所や意図的に曖昧な表現にしている箇所が多数あります。


はじめに

FargateにEDRを導入するにあたり、AWSマネージドサービスであるGuardDutyを利用する方法もありますが、ここではサードパーティー製のEDRであるCrowdStrike Falconを利用する方法について話します。

EDRについてはウイルス対策製品の一種となります。
ウイルス対策製品はシグネチャと呼ばれるウイルスのパターンと合致したものを検知、Blockする仕組みですが、EDRは挙動ベースの対策製品となります。
具体的に言うと、ランサムウェアのようにファイルを暗号化するという挙動を検知し、動作をBlockしたり、通常では起こりえないレジストリの書き換えなどの動作を検知し、Blockする仕組みとなります。
マクロを実行した際にボットネットと呼ばれるダウンロードサイトから攻撃ツールをダウンロードさせ、実行するようにレジストリに登録するなどがあります。
EDRでは未知のマルウェアだとしても怪しい動きを検知して止めるため、ウイルス対策製品に比べてより安全性が高いと言われています。

※上記EDRの説明及び図について弊社セキュリティ部門から解説を頂きました。この場を借りて感謝申し上げます。

本記事内のリンクラベルに「公式ドキュメント」と記載のあるものは、参照にCrowdStrikeのアカウントが必要です。ご留意ください。


構成図

asiaリージョンからのアクセスする想定の構成図です。CrowdStrike社のAWSリージョンはusとeuのみです。
NATゲートウェイとインターネットゲートウェイは使用しません。

尚、2024年11月頃にAWS PrivateLinkがクロスリージョン接続をサポートするようになりましたが、2025年1月時点ではCrowdStrike社のVPCエンドポイントではサポートしていませんでした。


導入手順 ネットワーク編

(1). CrowdStrikeサポートに連絡し、自社のAWSアカウントIDをホワイトリストに追加してもらう

下記ページの「What to provide support?」に記載されている通り、サポートに連絡します。必要な情報は以下3点。
(公式ドキュメント)How to setup CrowdStrike platform with AWS PrivateLink

  • Falcon管理者ロールを持つCrowdStrikeサポートのユーザー
  • センサーのダウンロードページにあるCustomer ID
  • プライベートリンクを有効にしたいAWSアカウントID

「センサーのダウンロードページにあるCustomer ID」はCrowdStrikeサポートページで確認できます。下記画像の右上から順番に遷移していく事で確認できます。

(2). 自社AWSアカウントのリージョンAにVPCとVPCエンドポイントを作成する

自社AWSアカウントのリージョンAにVPCを作成します。リージョンAはCrowdStrikeとの契約に合わせてください。例えばus-west-1等。(既に存在する場合はスキップ)

次に下記ページの「CrowdStrike Falcon VPC Service Endpoints」に記載されているリージョンAに対応したVPCエンドポイントを、自社AWSアカウントに3点作成します。
(公式ドキュメント)How to setup CrowdStrike platform with AWS PrivateLink

  • Sensor Proxy
  • Download Server
  • Upload Server

Web上のVPCエンドポイント作成で「サービスの検証」で正常に検証されるか確認する事をお勧めします。
もし検証に失敗した場合、(1)のホワイトリスト登録が正常であるか問合せを検討してください。

(3). VPCペアリング接続を設定(asiaリージョン→リージョンA)

既に自社AWSアカウントのasiaリージョンにVPCが作成されている事を前提にしています。
特筆すべきことはないため省略します。

(4). リージョンAでルート設定

VPCのルートテーブルに対してVPCペアリング接続のルートを設定してください。

(5). プライベートホストゾーンを作成

自社AWSアカウントのasiaリージョンからcloudsink.net.に対するホストゾーンを作成し、そこに(2)で作成したVPCエンドポイントに対するAレコードを作成します。

リージョンAがus-west-1の場合は以下のようになります。

レコード名 タイプ エイリアス ルーティング先
cloudsink.net NS いいえ 省略
cloudsink.net SOA いいえ 省略
lfodown01-b.cloudsink.net A はい リージョンAのVPCエンドポイントのDNS名
(例: vpce-xxxx.vpce-svc-xxxx.us-west-1.vpce.amazonaws.com.)
Upload ServerのDNS(※) A はい 同上
ts01-b.cloudsink.net A はい 同上

※こちらの情報はCrowdStrikeのGithubリポジトリで公開されている情報ですが、Upload Serverだけ公開されていません。「(公式ドキュメント)How to setup CrowdStrike platform with AWS PrivateLink」の方に記載されている情報を参考にしてください。

(6). 接続確認(リージョンA → CrowdStrike)

EC2等からホスト名解決できるか確認します。
正常に接続できる場合、(2)で作成したリージョンAのプライベートIPアドレスが返却されます。
もしパブリックIPアドレスが返却される場合、ルーティングやNATゲートウェイやインターネットゲートウェイの存在を確認してください。

以下はリージョンAがus-west-1だった場合の確認コマンドです。

getent hosts ts01-b.cloudsink.net
getent hosts lfodown01-b.cloudsink.net
getent hosts Upload ServerのDNS

(7). 接続確認(asiaリージョン → CrowdStrike)

(6)と同様です。


導入手順 ECR編

CrowdStrikeセンサーのDockerイメージ取得部分だけインターネット接続が必要になります。
自身のインターネット接続可能な環境でイメージ取得する、もしくはVPC外で同様の事を実施する等してください。

(1). CrowdStrikeのサポートページでAPIクライアントを登録

まずはサポートページの右上ナビゲーションバーから「サポートとリソース → API クライアントとキー → APIクライアントの作成」の順に進みます。

API作成ページでAPIの名称や説明を入力し、スコープから[Falcon Images Download]の[Read]を選択して作成します。
作成後、クライアントIDとシークレットが表示されるため、そちらを手元に控えます。

(2). CrowdStrikeセンサーのDockerイメージ取得

下記ドキュメントの「手順3:センサーイメージを取得し、ECRにプッシュする」の「2.Falcon Container Sensorプルスクリプトを実行して、最新のイメージをフェッチします。 」までの手順にてDockerイメージを取得できます。
(公式ドキュメント)Linux用FalconコンテナセンサーをECSFargateにデプロイする

本章の冒頭でインターネット接続が必要と記載した理由は、ここの手順で実行されるShellScriptにてCrowdStrike社のDockerレジストリやAPIを叩いているためです。
例えばAPIはShellScriptのこの辺にドメインが記載されており、リージョンによって切り替えているようです。

(3). ECRへCrowdStrikeセンサーのDockerイメージをPush

DockerイメージのECRへの登録は自身の環境に適した方法で実施してください。
AWSアカウント内でインターネット接続が許容されている場合は、ドキュメントの続きの手順で良いと思います。
そうでない場合はDockerイメージをファイルへ保存し、非インターネット環境上にアップロードし、手動でECRへPushする等、工夫してください。


導入手順 Fargate編

(1). CrowdStrikeセンサーを使ってECSタスク定義を修正する

CrowdStrikeセンサーについては下記補足を参照。
下記ドキュメントの「手順5:ECSタスク定義パッチ適用ユーティリティを実行する」と「手順6:Linux用 FalconコンテナセンサーをECS Fargateにデプロイする」を実施することで、ECSタスク定義を登録または更新します。
(公式ドキュメント)Linux用FalconコンテナセンサーをECSFargateにデプロイする

(2). ECSタスク定義を実行し、監査ログが出力されることを確認する

監査ログが出力されているかはサポートページで確認できます。

もしECSタスク実行後にCrowdStrikeの監査ログが出力されていない場合、修正後のECSタスク定義の環境変数FALCONCTL_OPTSに--trace=infoを追加すると詳細なCloudWatchログが出力されるようになり、調査の役に立ちます。

"environment": [
   {
      "name": "FALCONCTL_OPTS",
      "value": "--trace=info --cid=XXXXX-YY"
   }
],

私の場合、ECSタスクは正常終了していましたがCrowdStrikeの監査ログが確認できない事がありました。
上記のtraceオプションを有効にした結果、CloudWatchにてCrowdStrikeのDNS名前解決が失敗しているログが出力され、ネットワーク設定に誤りがあった事が分かりました。


CrowdStrikeセンサーについて補足

CrowdStrikeセンサーのDockerイメージには役割が二つあります。

(1). ECSタスク定義生成機能

ECSタスク定義テンプレートを入力として渡し、CrowdStrikeセンサーコンテナをサイドカーとして実行するよう修正したものを出力として返却しています。
CrowdStrikeセンサーコンテナではENTRY_POINTを使用する前提であるため、メインコンテナのDockerイメージはENTRY_POINTを使用するようビルドしてください。
以下は修正前後のECSタスク定義テンプレートです。

修正前

{
    "networkMode": "awsvpc",
    "containerDefinitions": [
        {
            "name": "XXXX",
            "image": "XXXX",
            "cpu": XXXX,
            "memory": XXXX,
            "portMappings": [
                {
                    "name": "XXXX",
                    "containerPort": XXXX,
                    "hostPort": XXXX,
                    "protocol": "XXXX",
                    "appProtocol": "XXXX"
                }
            ],
            "essential": true,
            "environment": [],
            "environmentFiles": [],
            "mountPoints": [
                {
                    "sourceVolume": "XXXX",
                    "containerPath": "/XXXX",
                    "readOnly": false
                }
            ],
            "volumesFrom": [],
            "ulimits": [],
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-create-group": "true",
                    "awslogs-group": "XXXX",
                    "awslogs-region": "XXXX",
                    "awslogs-stream-prefix": "ecs"
                },
                "secretOptions": []
            },
            "systemControls": []
        }
    ],
    "family": "XXXX",
    "taskRoleArn": "XXXX",
    "executionRoleArn": "XXXX",
    "volumes": [
        {
            "name": "XXXX"
        }
    ],
    "placementConstraints": [],
    "requiresCompatibilities": [
        "FARGATE"
    ],
    "cpu": "XXXX",
    "memory": "XXXX",
    "ephemeralStorage": {
        "sizeInGiB": XXXX
    },
    "runtimePlatform": {
        "cpuArchitecture": "XXXX",
        "operatingSystemFamily": "XXXX"
    }
}

修正後

{
   "containerDefinitions": [
      /**** メインコンテナの定義 ****/
      {
         "cpu": XXXX,
         "dependsOn": [
            {
               "condition": "COMPLETE",
               "containerName": "crowdstrike-falcon-init-container"
            }
         ],
         "entryPoint": [
            "/xxxx/yyyy/CrowdStrike/rootfs/lib64/ld-linux-x86-64.so.2",
            "--library-path",
            "/xxxx/yyyy/CrowdStrike/rootfs/lib64",
            "/xxxx/yyyy/CrowdStrike/rootfs/bin/bash",
            "/xxxx/yyyy/CrowdStrike/rootfs/entrypoint-ecs.sh",
            /**** ここにDockerFileのエントリポイントが記載される ****/
            "python",
            "/tmp/helloworld.py"
         ],
         "environment": [
            {
               "name": "FALCONCTL_OPTS",
               "value": "--cid=XXXXX-YY"
            }
         ],
         "environmentFiles": [],
         "essential": true,
         "image": "XXXX",
         "linuxParameters": {
            "capabilities": {
               "add": [
                  "SYS_PTRACE"
               ]
            }
         },
         "logConfiguration": {
            "logDriver": "awslogs",
            "options": {
               "awslogs-create-group": "true",
               "awslogs-group": "XXXX",
               "awslogs-region": "XXXX",
               "awslogs-stream-prefix": "ecs"
            },
            "secretOptions": []
         },
         "memory": XXXX,
         "mountPoints": [
            {
               "containerPath": "/XXXX",
               "readOnly": false,
               "sourceVolume": "XXXX"
            },
            {
               "containerPath": "/xxxx/yyyy/CrowdStrike",
               "readOnly": true,
               "sourceVolume": "crowdstrike-falcon-volume"
            }
         ],
         "name": "XXXX",
         "portMappings": [
            {
               "appProtocol": "XXXX",
               "containerPort": XXXX,
               "hostPort": XXXX,
               "name": "XXXX-XXXX-XXXX",
               "protocol": "XXXX"
            }
         ],
         "systemControls": [],
         "ulimits": [],
         "volumesFrom": []
      },
      /**** サイドカーのCrowdStrikeの定義 ****/
      {
         "entryPoint": [
            "/bin/bash",
            "-c",
            "chmod u+rwx /xxxx/yyyy/CrowdStrike && mkdir /xxxx/yyyy/CrowdStrike/rootfs && cp -r /bin /etc /lib64 /usr /entrypoint-ecs.sh /xxxx/yyyy/CrowdStrike/rootfs && chmod -R a=rX /xxxx/yyyy/CrowdStrike"
         ],
         "essential": false,
         "image": "XXXX",
         "mountPoints": [
            {
               "containerPath": "/xxxx/yyyy/CrowdStrike",
               "readOnly": false,
               "sourceVolume": "crowdstrike-falcon-volume"
            }
         ],
         "name": "crowdstrike-falcon-init-container",
         "readonlyRootFilesystem": true,
         "user": "0:0"
      }
   ],
   "cpu": "XXXX",
   "ephemeralStorage": {
      "sizeInGiB": XXXX
   },
   "executionRoleArn": "XXXX",
   "family": "XXXX",
   "memory": "XXXX",
   "networkMode": "awsvpc",
   "placementConstraints": [],
   "requiresCompatibilities": [
      "FARGATE"
   ],
   "runtimePlatform": {
      "cpuArchitecture": "XXXX",
      "operatingSystemFamily": "XXXX"
   },
   "taskRoleArn": "XXXX",
   "volumes": [
      {
         "name": "XXXX"
      },
      {
         "name": "crowdstrike-falcon-volume"
      }
   ]
}

(2). EDR機能

①で出力されたECSタスク定義テンプレートを使用し、タスク実行するとCrowdStrikeセンサーコンテナがサイドカーとして実行され、ログがCrowdStrikeのAWSアカウントへ送信されます。

修正後のECSタスク定義テンプレートを見て分かるように、メインコンテナのENTRY_POINTがECSタスク定義側で上書きする形になっています。
上書きされたENTRY_POINTではサイドカーのCrowdStrikeセンサーコンテナをマウントさせたディレクトリ上にあるShellScriptを叩いており、ShellScriptの引数でメインコンテナのENTRY_POINTで実行していたコマンドを渡して実行される形になっています。


おわりに

CrowdStrike社のサポートページで公開されているドキュメントが豊富であるものの手順が多く、IaCやビルドの自動化を進めようとすると更に複雑となり大変でした。
また、スクリプトやCloudFormationテンプレートを記載した方が分かり易く、記事も充実するのですがセキュリティの関係上そうもいきませんでした。

試してはいませんがFargateにEDRを導入するのであればAWSマネージドサービスのGuardDutyが手軽で便利そうだなぁと個人的に思います。
オンプレミスやAWS以外のクラウドサービスを活用している組織であれば監査ログを集約する目的で特定のサードパーティー製EDRを導入する機会もあると思います。
この記事をご覧になった皆様の役に立てましたら幸いです。


JMDCでは、ヘルスケア領域の課題解決に一緒に取り組んでいただける方を積極採用中です!詳細は下記の募集一覧からご確認ください。 hrmos.co まずはカジュアルにJMDCメンバーと話してみたい/経験が活かせそうなポジションの話を聞いてみたい等ございましたら、下記よりエントリーいただけますと幸いです。 hrmos.co ★最新記事のお知らせはぜひ X(Twitter)、またはBlueskyをご覧ください! twitter.com bsky.app