RevComm Tech Blog

コミュニケーションを再発明し 人が人を想う社会を創る

Amazon Inspectorによるプラットフォーム診断とコンテナイメージ改善の取り組み

はじめに

RevComm の小門です。 普段はバックエンドエンジニアとしてプロダクトの開発に携わっています。

2022年12月まではインフラエンジニアとして全社横断的なシステムのセキュリティ強化対応を担当していました。
今回は、セキュリティ強化の一環として「プラットフォーム診断」およびそれに付随したコンテナイメージのセキュリティ改善対応について紹介します。

プラットフォーム診断

プラットフォーム診断とは OS やミドルウェアに既知の脆弱性が潜んでいないか洗い出すことを指します。

プラットフォーム診断として脆弱性を検知するにはスキャンツールが必要となります。 例えば OSS の脆弱性ツールとして下記のものがあります。

  • サーバー用
    • Vuls, OpenSCAP, OpenVAS
  • コンテナ用
    • Trivy, Clair

RevComm では主に AWS を使用してサービス提供しており、AWS のセキュリティサービスに Amazon Inspector というものがあります。 Inspector を使うと EC2、ECR を対象にした脆弱性スキャンを実現できます。

結論として、下記のポイントからプラットフォーム診断のツールとして Amazon Inspector を採用しました。

  • 機能要件を満たしている点
    • 仮想マシン(Amazon EC2)とコンテナ(Amazon ECR)のスキャンに対応している
  • マネージドである点
    • AWS の他サービスと連携して自動スキャンできる

運用に向けた課題

RevComm では Amazon ECS を利用して提供している Web アプリケーションのサービスがあります。 Inspector を利用するとスキャンが自動で実行されるため便利ですが、ECR イメージのスキャンに関してはデフォルトの機能だけでは運用を開始できない課題がありました。

1点目は、棚卸しすべきコンテナイメージ(ECR)の対象を絞り込むことが難しいことです。

サービスのリリースに伴って新しいコンテナイメージを ECR に保存し、タスク定義で指定するイメージも更新します。
Inspector の機能でリポジトリやタグなどの条件で ECR のイメージを絞り込むことはできますが、リリースの度に使用するイメージが変化する場合は特定が難しくなります。

例えば、ソースコードの git ハッシュ値をコンテナイメージのタグでバージョニングしていくケースが考えられます。
参考: Best Practices - Running your application with Amazon ECS - Amazon Elastic Container Service

As a best practice, tag container images with a unique tag for each build. We recommend that you tag your images using the git SHA for the git commit that was used to build the image.

また、開発と本番環境間で同じコンテナイメージを使用する方法として AWS アカウント間で ECR イメージを共有することができます(Resource based permission )。
これは複数の環境で同一コンテナイメージからアプリケーションを実行するのに便利ですが、この場合デフォルトだと Inspector で別アカウントの ECR イメージに関する情報が取得できません。
そのため、複数 AWS アカウント上の ECR イメージのスキャン結果を集約管理する必要があります。これが2点目です。

以上より、ECS サービスで使用するサービス提供のために実際に使用されるコンテナイメージのタグを適切に特定し、ECR リポジトリもアカウント跨ぎである可能性を考慮したツール整備と運用を検討しました。

実現した構成

上記の課題を解決するアプローチとして、本番環境で実稼働している ECS タスクの情報を取得し集計すべきコンテナイメージタグを抽出するようにしました。

具体的な構成イメージと処理概要を示します。

  1. AWS Organizations の機能と連携して「Inspector 管理アカウント」を設定する。
    …Inspector 管理アカウントでは全アカウントにまたがった Inspector スキャン結果を取得できる
  2. 本番環境で稼働している ECS タスクの情報から、実際に使用中のコンテナイメージを特定する
  3. 対象のコンテナイメージに関する脆弱性情報を Inspector 管理アカウント上でフィルタリング抽出して集計する

ポイントは 2. において本番環境で稼働中の ECS サービスおよび ECS タスク定義から集計すべきコンテナイメージを絞り込んでいる点です。
下記の API を組み合わせて集計するスクリプトを実装しました。

  • ecs list-clusters
  • ecs describe-services
  • ecs describe-task-definition

これにより、プロダクトとして稼働するサービスの実態を必要十分に集計することを実現しました。

コンテナイメージ改善

前述したプラットフォーム診断と合わせて、プロダクトサービスを提供するコンテナアプリケーションの脆弱性を改善する対応を実施しました。

具体的には、コンテナイメージが軽量な環境となるように Dockerfile を修正しました。 RevComm ではバックエンド言語として主に Python を使用しているため、Python を例にポイントとなる点を紹介していきます。

ベースイメージに slim イメージを使う

本番用のアプリケーション環境として使用する場合、ベースイメージとして slim イメージを使用します。

slim イメージと非 slim イメージ(例えば python:3python:3.11)の大きな違いはビルトツールや dev パッケージの有無です。

$ docker run -ti --rm python:3.11.1 apt list --installed | grep "^lib.*-dev" | wc -l
84

$ docker run -ti --rm python:3.11.1-slim apt list --installed | grep "^lib.*-dev" | wc -l
0

上記のように slim イメージには libxxx-dev といった dev パッケージが一切含まれていないことが分かります。

2 つのイメージを Amazon ECR の拡張スキャン機能でスキャンした結果を比較してみます。
※スキャン結果は 2023/2/2 時点のもの

  • python:3.11.1
  • python:3.11.1-slim(※脆弱性の検出なし)

不要なパッケージをインストールしない

セキュアなコンテナイメージを作り上げるためには不要なパッケージをインストールしないことが重要です。
結果的にイメージが軽量になり、可搬性も向上します。

例えば前述の slim イメージを使うと gcc が使えませんが、Dockerfile の中で gcc をインストールしてしまうのは良い方法ではありません。

FROM python:3.11.1-slim

RUN apt-get update \
  && apt-get -y install gcc  # <= ★No good

ビルド用に gcc が必要な場合があっても、実行時に gcc は(ほとんどの場合)不要だからです。

マルチステージビルド

アプリケーションに必要なビルド処理と軽量かつセキュアな実行環境を両立させるために有用なのがマルチステージビルドです。
※本記事ではマルチステージビルド自体の解説は割愛します。

Multi-stage builds | Docker Documentation

マルチステージビルドを使った Python 用の Dockerfile の例を紹介します。

サンプルとして、インストール時に C コンパイラが必要な uWSGI(WSGI 規格のアプリケーションサーバー)を slim イメージで利用するケースを考えてみます。

# -- stage: builder
FROM python:3.11.1 AS builder
RUN pip3 install --no-cache-dir uwsgi

# -- stage: runner
FROM python:3.11.1-slim AS runner
RUN apt-get update \
    && apt-get -y upgrade \
    && apt-get install -y \
      # for uWSGI
      libxml2

COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin

# uWSGI を使用して WSGI アプリケーションを起動できることを確認
COPY main.py ./
CMD ["uwsgi", "--http", ":8000", "--wsgi-file", "main.py"]
EXPOSE 8000

1つの Dockerfile の中でbuilderrunner、2つの「ステージ」を定義しています。

  • builder ステージ:uwsgi ライブラリをビルド・インストールする
  • runner ステージ:インストール成果物である site-packages 配下をコピーすることで使用できるようにする

main.py は HTTP レスポンスを返すための最小限のコードです。

def application(env, start_response):
    start_response('200 OK', [('Content-type', 'text/plain; charset=utf-8')])
    return [b'Hello World']

上記 Dockerfile をビルド・起動してレスポンスが受け取れることを確認してみます。

$ # ビルド対象ステージを --target で指定する
$ docker build . --target runner -t multistage-test

$ docker run -d --rm \
  --name multistage-test \
  -p 8000:8000 \
  multistage-test

$ # アクセス確認
$ curl localhost:8000
Hello World

$ docker kill multistage-test

本記事で紹介した Dockerfile の改善例については SpeakerDeck のスライドでより詳しく解説していますので、ぜひご覧ください。

まとめ

Amazon Inspector を使ったプラットフォーム診断の実践例と、コンテナイメージ改善の取り組みについて紹介しました。

コンテナイメージには必要最小限のパッケージ、ライブラリのみインストールするようにすることで軽量かつセキュアな環境を作成することができます。
また、新しい脆弱性は日々報告されていくため、一度作ったイメージは定期的に最新化する運用も重要です。

RevComm は電話営業や顧客対応を可視化する音声解析 AI 搭載型のクラウド IP 電話 MiiTel (ミーテル) を提供しています。
今後もお客様に安心して MiiTel をご利用頂けるよう、引き続きプロダクトのセキュリティ向上に取り組んでまいります。

また、エンジニアを積極採用中ですので、興味をお持ち頂けましたらぜひともご応募ください。

hrmos.co