Aqua Platform EnterpriseをECS-Fargate環境にデプロイしてランタイム検知してみた
こんにちは!AWS事業本部コンサルティング部のたかくに(@takakuni_)です。
今回は、ECS-Fargate環境にAqua Platform Enterpriseの「MicroEnforcer」をデプロイしコンテナのランタイム検知を実行してみようと思います。
Aqua Platform Enterpriseとは
Aqua Platformはaqua社が提供するソリューションの1つでコンテナとクラウドネイティブアプリのために開発された、フルライフサイクルなセキュリティソリューションになります。
MicroEnforcerとは
Aqua MicroEnforcerは、AWS Fargateのような**Containers-as-a-Service(CaaS)**環境で動作するコンテナにランタイムセキュリティを提供する機能です。
AWS Fargateでは、Amazon ECS on EC2に比べEC2のスケーリング、パッチ当てなどのホストの管理が不要になり、コンテナ開発により集中できるメリットがあります。
一方で、ホストの管理をAWSに任せているため、Enforcerとして一般的に使用する、「Aqua Enforcer」をデプロイできない制約などが存在します。
そのため、Aqua Securityでは、CaaS環境でEnforcerを使用するためのMicroEnforcerを提供しています。
デプロイ方法
MicroEnforcerをデプロイする方法は以下の2種類があります。
- アプリケーションコンテナのDockerfile内にMicroEnforcerのバイナリを埋め込み立ち上げる方法
- サイドカーコンテナからアプリケーションコンテナへMicroEnforcerの情報を渡しコンテナを立ち上げる方法
今回は、1の方法を採用してMicroEnforcerをデプロイしようと思います。
構成図
構成図は以下の通りです。
- サブネットを除く赤枠のリソースをCloudFormationで作成します。
- 各コンテナは以下のエンドポイントに到達する必要があるためNAT Gatewayを使用します。(VPC EndpointやInternal LoadBalancerを使用することでインターネットを経由しない構成も可能です。)
- Application Container:ECR、NLB
- Aqua Gateway:ECR
- Aqua Console:ECR、Secrets Manager
- Aqua Databaseは、CloudFormationのパラメターでマルチAZ可能になっていますが今回はシングルAZで実装します。
- 今回、ALB用に証明書をACMから発行しましたが発行しなくても実装は可能です。
検証用VPCの作成
すでに使用したいVPCが決まっていれば、この手順は飛ばしてもらって構いません。
構成図の通り、2AZ間でパブリック/プライベートサブネットを作成し、1つのAZでNAT Gatewayを作成する構成を作成しました。
図で表すと以下のリソースまで作成できました。
ECRの作成
ECSでコンテナを起動するためDocker Imageを保存するレジストリを作成します。
今回作成するレジストリは以下の3つです。
- aqua-console(Aqua ServerのDocker Image用)
- aqua-gateway(Aqua GatewayのDocker Image用)
- aqua-container(Application ContainerのDocker Image用)
同じように残り2つのレジストリも作成します。
イメージのプッシュ(Aqua Server/Gateway)
作業手順は以下の通りです。
- Dockerイメージの取得を行うためAquaにログインします。
docker image pull
でイメージを取得します。- ECRへログイン
docker image tag
でイメージにタグ付けを行います。docker image push
でイメージをECRにプッシュします。
# 1. Dockerイメージの取得を行うためAquaにログインします。
takakuni@~ % docker login registry.aquasec.com
Username: XXXXXXXXXXXX@example.com
Password:
Login Succeeded
# 2. `docker image pull`でイメージを取得します。
# aqua-console
takakuni@~ % docker pull registry.aquasec.com/console:6.5
# aqua-gateway
takakuni@~ % docker pull registry.aquasec.com/gateway:6.5
# 3. ECRへログイン(XXXXXXXXXXXXはアカウントID, リージョンも気をつけるポイント)
takakuni@~ % aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com
# 4. `docker image tag`でイメージにタグ付けを行います。
# aqua-console
takakuni@~ % docker image tag registry.aquasec.com/console:6.5 XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/aqua-console:latest
# aqua-gateway
takakuni@~ % docker image tag registry.aquasec.com/gateway:6.5 XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/aqua-gateway:latest
# 5. `docker image push`でイメージをECRにプッシュします。
# aqua-console
takakuni@~ % docker push XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/aqua-console:latest
# aqua-gateway
takakuni@~ % docker push XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/aqua-gateway:latest
図にすると以下のリソースまで作成できている状態になります。
CloudFormationの実行
以下のリポジトリからCloudFormationスタックの作成を行います。
今回使用するブランチは、「6.5」を使用しますが必要に応じてバージョンアップしたブランチをご使用ください。
今回はaquaFargate.yaml
を使用してデプロイします。イベント履歴を確認すると作成には15分程度要していました。
使用するパラメータの例は以下の通りです。
キー | 値 | 備考 |
---|---|---|
ActiveActive | false | |
AquaConsoleAccess | 0.0.0.0/0 | |
AquaGatewayImage | XXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/aqua-gateway | |
AquaServerImage | XXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/aqua-console | |
AuditRDS | No | |
AuditRdsInstanceClass | db.t3.medium | |
ClusterName | aqua-enterprise | 任意の値 |
EcsInstanceSubnets | 2AZにまたがるプライベートサブネットを2つ選択 | |
LBScheme | internet-facing | |
LbSubnets | 2AZにまたがるパブリックサブネットを2つ選択 | |
MultiAzDatabase | false | |
RdsInstanceClass | db.t3.medium | |
RdsStorage | 50 | |
SSLCert | ALBに紐づけるACMのARN | |
VpcCidr | 10.0.0.0/16 | |
VpcId | デプロイするVPCのID |
図にすると以下のリソースまで作成できている状態になります。
ALBのレコード登録
ACMにて証明書をリスナーに設定したので、設定したドメインでログインできるようにRoute53またはDNSサーバーのレコードを登録します。
私の場合は、Route53でドメインを管理しているため以下のエイリアスレコードを登録しました。
キー | 値 | 備考 |
---|---|---|
レコード名 | ACMと同じドメイン | |
レコードタイプ | Aレコード | |
エイリアス | 有効 | Route53でのみ使用可能 |
Application LoadBalancerのエイリアス | ALBのDNS名 |
Aqua Consoleへのログイン
Route53にレコード登録したドメインを経由で、ALBへログインを行います。
今回は検証のため以下のユーザーとパスワードで登録します。
キー | 値 | 備考 |
---|---|---|
ユーザー名 | administrator | |
パスワード | Admin1qaz@wsx |
問題なければライセンスの登録を行います。ライセンスの登録が完了したら、コンソール画面へ遷移します。
Enforcer Groupの作成
画面左ペインの「Administration」をクリックして、「Enforcers」をクリックします。
「Add Enforcer Group」をクリックして今回使用するMicroEnforcerのグループを作成します。
使用したパラメータは以下の通りです。
キー | 値 | 備考 |
---|---|---|
Enforcer Type | Aqua MicroEnforcer | |
Group Name | ECS-FargateMicroEnforcerGroup | |
Logical Name | ECS-FargateMicroEnforcerLGroup | |
Aqua Gateway | デフォルトの値(ip-10-0-135-9.ap-northeast-1.compute.internalなどの値) | |
Deployment Token | ECS-FargateMicroEnforcerDeploymentToken |
加えて、検知した際の挙動をブロックしたいため「Advanced settings」から「Enforcement Mode」を「Enforce」に変更します。
「Create Group」をクリックすると、Enforcer Groupが作成されます。
「Group Name」と「Logical Name」の違い
設定値でNameを設定する箇所が2箇所あり何が違うのかをざっくり説明します。
Group Name
Aquaコンソールから確認できる「Enforcer Group」の名前に当たります。
設定後は変更できないため命名には注意が必要です。
Logical Name
MicroEnforcerから確認するための「Enforcer Group」の名前に当たります。
主にMicroEnforcerを組み込むDockerコンテナの環境変数に指定します。設定後でも名前を変更できます。
また、Group NameとLogical Nameは1対1で紐づいており、複数のLogical Nameを紐づけることができないことも注意が必要です。
Application Containerの作成
Dockerfileの作成
今回は、アプリケーションコンテナとしてnginx
を立ち上げたいためdocker image pull
でイメージを取得します。
takakuni@~ % docker image pull --platform x86_64 nginx:1.21.6
また、本記事では起動するコンテナにMicroEnforcerを仕込み制御を行うため、Dockerfileを作成し、MicroEnforcerの組み込みを定義します。
作成するDockerfileに必要な情報は以下の通りです。
- MicroEnforcerバイナリ
- DockerイメージのCMD, ENTRYPOINTの値
MicroEnforcerバイナリの取得
以下、URLへアクセスし「ユーザー名」、「パスワード」を入力後、ダウンロードを行います。
CMD, ENTRYPOINTの取得
Dockerイメージで設定されたCMD,ENTRYPOINTの値を探します。
取得したnginxイメージに対して以下のコマンドを入力します。
takakuni@~ % docker image inspect nginx:1.21.6 -f "{{json .Config.Cmd}}"
# 期待値:["nginx","-g","daemon off;"]
takakuni@~ % docker image inspect nginx:1.21.6 -f "{{json .Config.Entrypoint}}"
# 期待値:["/docker-entrypoint.sh"]
Dockerfileのでき上がり例
仕上がり例としては以下のようなYAMLファイルになります。
FROM nginx:1.21
# ダウンロードしたmicroenforcerを追加
ADD microenforcer /bin/microenforcer
RUN ["chmod", "+x", "/bin/microenforcer"]
RUN ["/bin/microenforcer", "aqua-init"]
# 取得した値を記載
CMD ["nginx","-g","daemon off;"]
ENTRYPOINT ["/bin/microenforcer","/docker-entrypoint.sh"]
ビルド
以下のコマンドでDockerイメージのビルド、ECRへのプッシュを行います。
# ECRへログイン
takakuni@~ % aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com
# イメージのビルド
takakuni@~ % docker image build --platform x86_64 -t aqua-container .
# タグ付け
takakuni@~ % docker image tag aqua-container:latest XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/aqua-container:latest
# プッシュ
takakuni@~ % docker image push XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/aqua-container:latest
タスク定義の作成
実際にコンテナを起動するためにタスクを定義します。
タスク定義では、以下の環境変数を起動するコンテナに渡し、MicroEnforcerとAqua Gatewayを通信できるように設定します。
- AQUA_TOKEN:MicroEnforcerとEnforcerグループ間で認証するためのトークン
- AQUA_SERVER:Aqua GatewayのIPアドレス:8443またはNLBのDNS名:8444
- AQUA_LOGICAL_NAME:Enforcerグループで指定したLogical Name
なお、上記の環境変数はEnforcerグループの管理画面より確認できます。
AQUA_SERVERの設定値について
AQUA_SERVERの設定値についてですが以下の2種類の方法が考えられます。
- Aqua GatewayのIPアドレスにPort:8443で直接アクセス
- NLBのDNS名に対してPort:8444でアクセス
Aqua Gatewayが何かしらの原因で落ちてしまった際に共倒れを防ぐため今回は、NLBのDNSに対してPort:8444で設定を行います。
「環境、ストレージ、モニタリング、タグの設定」では、IAMロールを「ecsTaskExecution」に変更し次に進みます。
確認画面の結果も念の為載せておきます。問題なければ「作成」をクリックします。
セキュリティグループの作成
サービスを立ち上げるため、アプリケーションコンテナ用にセキュリティグループを設定します。
今回は、タスク定義でマッピングした80番ポートを開くように設定しました。
サービスの起動
必要な情報が揃ったので実際にサービスを起動します。
注意が必要なポイントは以下になります。
- 「リビジョン」は意図したバージョン(最新など)ですか
- 「サブネット」はプライベートサブネットを選択
- 「セキュリティグループ」はさきほど作成したものを選択
- パブリックIPはNAT Gatewayを経由するため必要なし
サービスの起動が完了すると以下、Enforcer管理画面からコンテナがグループに登録されていることを確認できます。
以上でMicroEnforcerの設定は完了しました。
ランタイム検知(動作確認)
Runtime policyの編集
動作確認のため、「Drift Prevention」を使用して、元のコンテナイメージにないファイルの実行が防止されるか確認してみます。
Aqua Consoleから「Policy」、「Runtime Policies」を選択します。
「Aqua default runtime policy」を選択して編集します。
「Enforcement Mode」を「Enforce」にして「Save」をクリックします。
再起動を求められる画面で、「OK」を選択します。
ECS Execの有効化
今回はESC ExecからRuntime検知を行うため有効化設定を行います。
初めに、IAMロール「ecsTaskExecution」に以下のポリシーをインラインまたはIAMポリシーを新規作成してアタッチします。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssmmessages:CreateControlChannel",
"ssmmessages:CreateDataChannel",
"ssmmessages:OpenControlChannel",
"ssmmessages:OpenDataChannel"
],
"Resource": "*"
}
]
}
AWS CLIで「ECS Execの有効化」を行います。
今回は、以下ブログを参考にしました。
# 環境変数定義
CLUSTERNAME=$(aws ecs list-clusters --query 'clusterArns[0]' --output text)
SERVICENAME=$(aws ecs list-services --cluster $CLUSTERNAME --query 'serviceArns[-1]' --output text)
TASKNAME=$(aws ecs list-tasks --cluster $CLUSTERNAME --service-name $SERVICENAME --query "taskArns[0]" --output text)
CONTAINERNAME=$(aws ecs describe-tasks --cluster $CLUSTERNAME --tasks $TASKNAME --query "tasks[].containers[].name" --output text)
# ECS Exec有効化
aws ecs update-service --cluster $CLUSTERNAME --service $SERVICENAME --enable-execute-command | grep enableExecuteCommand
# ECS Exec有効化したタスクを実行
aws ecs update-service --cluster $CLUSTERNAME --service $SERVICENAME --task-definition "aqua-container" --force-new-deployment
# 環境変数更新
TASKNAME=$(aws ecs list-tasks --cluster $CLUSTERNAME --service-name $SERVICENAME --query "taskArns[0]" --output text)
# ECS Execでコンテナに接続
aws ecs execute-command --cluster $CLUSTERNAME --task $TASKNAME --container $CONTAINERNAME --interactive --command "/bin/sh"
ECS Execでログイン後、「Drift Prevention」が効いていることを確認できました。
takakuni@~ % aws ecs execute-command --cluster $CLUSTERNAME --task $TASKNAME --container $CONTAINERNAME --interactive --command "/bin/sh"
The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.
Starting session with SessionId: ecs-execute-command-XXXXXXXXXXXXXXXXX
# cp -p /bin/pwd /bin/pod
# pod
Permission denied
参考
まとめ
以上、「Aqua Platform EnterpriseをECS-Fargate環境にデプロイしてランタイム検知してみた」でした。
CaaS環境でもコンテナセキュリティを実装したいとご検討のお客様はぜひAqua Enterpriseも選択肢の1つとしてご検討いただけますと幸いです。
以上、AWS事業本部コンサルティング部のたかくに(@takakuni_)でした!