AWS公式ドキュメントを参考にECS Fargateのセキュリティを考える

2024/04/01に公開

概要

こんにちは🙌
今回は、ECSのセキュリティ全般を公式ドキュメントのベストプラクティス + NIST アプリケーションコンテナセキュリティガイド(IPAによる翻訳監修)を参考にしながら考えてみて、整理していきたいと思います。

  • Amazon ECSベストプラクティス セキュリティ
  • NIST アプリケーションコンテナセキュリティガイド

ゴール

ECS Fargateを利用するにあたってのセキュリティを理解する。

責任共有モデル

AWSではよく出てきますが、セキュリティを考える上でも責任共有モデルを踏まえて、どのレイヤーに対して利用者として管理すべき必要があるのかが前提となります。
ECSでEC2を使うかFargateを使うかで範囲が異なります。ここではFargateに絞って紹介します。

公式ドキュメントで紹介されている画像の通りですが、APPLICATION、CONTAINER、TASK、NETWORK、DATA、IAMが責任範囲となっています。
それ以外はAWSによって管理されていますので、セキュリティに関しても委ねることが可能です。

IAM

ECSへのアクセス管理

どのAWSサービスでも同様の考え方となりますが、ECSへのアクセス管理は最小特権とすることです。できる限り必要とされる特定のアクションのみを許可するようなポリシーが推奨されています。
また、ECS APIへのアクセスを定期的に監査することも推奨されています。
これは、セキュリティ監査ガイドラインとして公式ドキュメントが用意されています。

ECSタスクのIAMロール

続いてタスクに割り当てるロールに関してとなります。
こちらも最小権限でポリシーを設定し、ロールを割り当てることが基本となります。
ドキュメントの中では、ECSがどのようにトークンを生成し、シークレットを保持するのかまで詳しく説明が書かれていて興味深いです。

ネットワークセキュリティ

ここでのトピックは大きく分けて以下の3点になります。

  • サービス間通信
  • セキュリティグループ
  • VPCエンドポイント
    ※Fargate前提で考えているので、ネットワークモードはVPCのみ、ECSエンドポイントは不要となります。

サービス間通信

AWS App Meshを使うことで、サービス間の通信に求められる機能をEnvoyプロキシで実装することができ、ACMを使ったTLS通信を実現させることが出来ます。(マイクロサービスの管理制御としてよく使われるサービス)
APP Meshの設定画面

また、サービス間だけではなく、ELBとの通信にもTLS通信を実装します。
複数の異なるドメインのサービスがホストされている場合、SNI(Sever Name Indication)によって、共通名不一致のエラーを防ぐことも可能です。これはALBに実装されています。
Elastic Load Balancing: Application Load Balancers では現在、複数の SSL 証明書や Server Name Indication (SNI) を使用するスマート証明書セクションをサポートしています。

セキュリティグループ

ECS Fargateでは、必然的にawsvpcモードを選ばざるを得ないので、セキュリティグループを使うことになります。
ドキュメントでは、タスクにENIがアタッチされるので、それに設定されるセキュリティグループでInboundとOutboudのトラフィックをコントールするように書かれています。

ネットワークトラフィックの分離(コンテナセキュリティIPA)

IPA資料では、機微性のレベルごとに仮想ネットワークを分離するように記載があります。
社外に公開するアプリケーションと社内利用アプリはネットワークを分けて、それぞれは明確に定義した通信のみが行われるようにします。

また、コンテナからの無制限ネットワークアクセスへの対策についても言及がされています。
ネットワークトラッフィックを制御し、特定のホストからインターネットへの通信に限定することやトラフィックのモニターが必要であるとされます。

VPCエンドポイント

AWSサービス間との通信はインターネットを経由せずにVPCエンドポイントで内部通信とします。また、VPCエンドポイントポリシーでは、限られた必要なリソースに絞ることが推奨されています。

{
  "Statement": [
    {
      "Sid": "LimitECRAccess",
      "Principal": "*",
      "Action": "*",
      "Effect": "Allow",
      "Resource": "arn:aws:ecr:region:account_id:repository/*"
    },
  ]
}

シークレット管理

ECSにおいて、シークレットな情報(パスワードやAPIキー等)を扱う場合、タスク定義でenvironmentとしてそのまま渡さずにsecretsとしてAWS Secrets Managerもしくは、Systems Manager Parameter Storeに格納した値を利用することが推奨されます。

ちなみにSecrets ManagerとParameter Storeの違いとしては、
Secrets Managerでは、自動的なローテーション、ランダムなシークレットの生成が出来ます。ただし、SecretsManagerの方が料金が高く設定されています。

Secrets Manager:シークレットあたり0.40USD/月
Parameter Store:標準 料金なし、アドバンスド 0.05USD/月
(2024年3月時点)

厳格なシークレット管理

タスク定義でsecretsを使った場合でも、コンテナでは環境変数として扱われることになります。これを避けたい場合の手段についても紹介されています。

S3バケットからの取得

暗号化されたS3にバケットにシークレットを保存します。
アプリケーションの起動もしくは実行時にS3から直接シークレットを読み取ることで環境変数としてコンテナに格納されずに値を扱うことが出来ます。

サイドカーコンテナを使って共有ボリュームをマウント

サイドカーコンテナを利用することによっても同様にアプリケーションコンテナの環境変数を介したシークレットの管理をしない形を取ることが出来ます。

また、シークレットの動的な更新にもサイドカーの再デプロイのみで済んだり、アプリケーションで使いたいシークレットが増えた場合に、コード変更をせずに実装することを可能とします。

上記のいずれを選択するにせよ、実装によっては、アプリケーションログにシークレットが記録されてしまったり、プロセス制御が出来るユーザーによってシークレットにアクセスされてしまうリスクは残ります。

ログ記録とモニタリング

awslog ログドライバー

ECSタスク定義でawslogログドライバーを使い、CloudWatchロググループ送信先を定義することで簡単にコンテナログを取得することが可能です。

Fluent Bit

対して、Fluent Bitを用いる場合についても説明があります。
ここでは簡単な説明となりますが、Fluent Bitを使うことによって、
CloudWatch Logs以外への出力、ログのフィルタリングや加工といったことが柔軟に行うことができるようになることがメリットとしてあげられます。
ECRのパブリックギャラリーにも用意されているので、導入自体は楽に出来る印象です。

タスクとコンテナのセキュリティ

コンテナイメージについての説明がメインとなります。イメージについては、書きれないほどに考慮すべき事項があるため、別途また記事でまとめてみようと思っています。
ここではいくつかを抜粋して記載してみます。

最小のイメージを使う

Distrolessなイメージ、つまり、アプリケーション実行に最低限の依存関係のみを含むイメージを使うことで脆弱性となりうるコンポーネントを減らすことが出来ます。
Googleによって提供されている以下のリポジトリを活用することで、Distrolessなコンテナイメージを利用出来ます。

Dockerでは、scratchを使うことで最低限のイメージ(中身のないイメージ)となります。
そのまま公式ドキュメントから持ってきていますが、Goアプリケーションビルドの例となります。
STEP1で、ビルド環境を設定、実行可能なバイナリをコンパイルし、
STEP2では、scratchをベースイメージとして、バイナリのみをコピーしています。

############################
# STEP 1 build executable binary
############################
FROM golang:alpine AS builder
# Install git.
# Git is required for fetching the dependencies.
RUN apk update && apk add --no-cache git
WORKDIR $GOPATH/src/mypackage/myapp/
COPY . .
# Fetch dependencies.
# Using go get.
RUN go get -d -v
# Build the binary.
RUN go build -o /go/bin/hello
############################
# STEP 2 build a small image
############################
FROM scratch
# Copy our static executable.
COPY --from=builder /go/bin/hello /go/bin/hello
# Run the hello binary.
ENTRYPOINT ["/go/bin/hello"]
This creates a container image that consists of your application and nothing else, making it extremely secure.

イメージスキャンによる脆弱性検出

イメージに脆弱性が含まれていないか定期的にスキャンを行います。
ECRのイメージスキャンでは、clairがAWSから提供されており、利用することが出来ます。

他にもOSSツールであればtrivyやDockerを使ってdocker scanによるイメージの脆弱性スキャンをすることも可能です。

このあたりは別の機会に試してみようと思っています。

特殊なファイルアクセス権の削除

特殊なファイルアクセス権とされるsetuidsetgidはイメージから削除します。
これらのアクセス権限が悪用されてしまった場合、スーパーユーザーのアクセス権を取得される可能性があります。

setuid:設定された実行可能ファイルは、そのファイルの所有者の権限で実行されます。つまり、rootユーザー権限でプログラムを実行することが出来ます。
setgid:設定された実行可能ファイルは、そのファイルのグループ権限で実行されます。setuidと同様に所有グループの権限でプログラムが実行されることになります。

rootユーザーでコンテナを実行しない

コンテナはセキュリティ向上のため非rootユーザーで実行することが推奨されます。
DockerfileにUSERディレクティブがなければ、デフォルトでコンテナはrootユーザーで実行されます。CI/CDパイプラインでは、USERディレクティブの有無をチェックするためにDockerfile-lintHadolintのようなツールを使用することで、ビルド時のチェックが可能です。

イメージ内の脆弱性への対応(多層防御)IPA資料より

複数レイヤに対しての多層防御が必要になってくると書かれています。

  1. デプロイのプロセスの早い段階で脆弱なイメージを検知し、脆弱なイメージのデプロイを防止するための制御を実施することで、本番環境に脆弱性が持ち込まれるのを防ぐことができる。
  2. コンテナに対応したネットワークモニタリングとフィルタリングにより、他のシステムをマッピングしようとする際に、他のコンテナへの異常な接続を検知する。
  3. コンテナに対応したプロセスモニタリングとマルウェア検知により、無効または予期しない悪意のあるプロセスの実行と、それらが環境に取り込むデータを検知する。

今回ここで記載してきた脅威への対応、緩和策がAWS公式ドキュメントと同様にまとめられていますね。

おわりに

ECSを使うことによってオーケストレーション管理をAWSに任せることが出来るようになったとはいえ、利用者側でコントールしなければならないセキュリティリスクは、把握しておく必要があることが分かりました。

ここであげた全てのセキュリティ要件を満たすことは、実際には困難な場合も多いようにも思えます。システム要件に合わせて取捨選択をすること、ベストプラクティスを把握しながらリスクを軽減を検討することで良い設計に繋げていきたいです。

以上となります。ありがとうございました🙌

Discussion