Skip to main content

Dockerを使ってPythonアプリケーションをコンテナ化するためのベストプラクティス

著者

Daniel Campos Olivares

blog feature snyk python security

2021年11月11日

0 分で読めます

PythonのDockerコンテナに関するブログを数多く読んだところ、その大半は、フレームワーク(Django、Flask、Falconなど)に依存しない形でPythonアプリケーションをコンテナ化する例を紹介していました。たとえば、次のようなものです。

FROM python
WORKDIR /usr/app
COPY . .
RUN pip install -r requirements.txt
CMD [ "python", "app.py" ]

このDockerfileを使えば、Python Flaskアプリケーションをビルドして実行できます。

docker build -t flask-application .
docker run -p 8080:5000 flask-application

簡単な2つの手順で、問題なく動きますよね?

この例はシンプルで、デモや入門チュートリアルには役立ちますが、重要な懸念事項の多くに対処できていません。そこでこの記事では、そうした懸念に対処し、Dockerを使ってPythonアプリケーションをコンテナ化する際の6つのベストプラクティスを紹介します。具体的には、次の点を解説します。

  1. コンテナ化したPythonアプリケーションには、明示的かつ再現性の高いDockerベースイメージのタグを使う

  2. 依存関係とソースコードを分離する

  3. 本番環境ではPython WSGIを使う

  4. コンテナは最小限の権限で実行し、rootでは決して実行しない

  5. アプリケーションの異常な状態に対処する

  6. Python Dockerアプリケーションイメージのセキュリティ脆弱性を検出して修正する

1. コンテナ化したPythonアプリケーションには、明示的かつ再現性の高いDockerベースイメージのタグを使う

Docker化したPythonアプリケーションのベースイメージにpythonを使うのは理にかなっているように思えますが、どのバージョンのPythonが使われるのかという問題が残ります。

この記事の執筆時点では、前述のDockerfileのベースイメージはPython 3.10を含むイメージを指しています。なぜでしょうか。特定のタグを指定していないため、デフォルトでそのベースイメージの:latestが使われるからです。Docker Hubの公式イメージページを見ると、これは3.10に当たります。

コンテナ化するPythonのバージョンを制御するには、Dockerfileで必ずバージョン情報を指定する必要があります。

Python 3.10を使いたいので、Dockerfileに:3.10タグを追加すればいいんですよね?

うーん……そう簡単ではありません。

Dockerのベースイメージタグ:3.10は、Python 3.10がインストールされた完全なオペレーティングシステムで、おそらく使うことのないライブラリが大量に含まれています。ソフトウェアが大量に含まれていることで、こうしたライブラリに存在する脆弱性によって攻撃対象領域が広がるという副作用もあります。

容量の大きなPython Dockerイメージを使うと、含まれるすべてのライブラリのバージョンを最新に保ち、メンテナンスするのが難しくなります。

Snyk Advisorを使ってPythonのベースイメージを調べると、Dockerベースイメージpython:3.10には、重大度が高い問題が12件、中程度が27件、低い問題が132件あることがわかります。つまり、Python Dockerイメージには、何も追加していない状態で最低でも171件のセキュリティ脆弱性が含まれることになります。

また、実質的にオペレーティングシステム全体を導入することになるため、Pythonアプリケーションサーバーのベースイメージはかなり大きくなり、ビルドが遅くなるうえ、より多くのディスク容量が必要になります。ベースイメージを選ぶ際の基本的なルールはいくつかありますが、特に重要なのは次の2つです。

Python Dockerイメージを選ぶ際のベストプラクティス

  1. 要件を満たす最小のベースイメージを選び、その上に構築しましょう。イメージが小さいほど脆弱性が少なく、リソース消費を抑えられ、不要なパッケージも少なくなります。

  2. 名前付きタグを使うだけでは、常に同じベースイメージを使えるとは限りません。それを保証する唯一の方法は、イメージダイジェストを使うことです。

この知識を踏まえて、もう一度Snyk Advisorを確認し、推奨される代替タグを見てみましょう。このツールでは、ベースイメージの脆弱性やサイズを一覧できるため、選択の際に大いに役立ちます。

アプリケーションでPython 3.10を使いたいので、そのバージョンのタグを探します。

公式Python DockerイメージのSnyk Advisorページ。docker pullコマンドと、深刻度・更新状況・サイズのデータを含む代替タグの推奨が表示されています

前述の脆弱性に加え、このイメージのベースサイズは約350 MBで、Debian 11をベースとしており、427個のパッケージがインストールされていることがわかります。この小さなPythonアプリケーションには、少し過剰だと言えるでしょう。

一方、Python用Dockerベースイメージには:3.10-slimもあります。重大度が高い問題は1件、中程度は1件、低い問題は35件です。ベースサイズは46.2 MBで、こちらもDebian 11をベースとしたオペレーティングシステムであり、インストール済みパッケージは106個です。デフォルトのイメージの代わりにこのDockerベースイメージをPythonアプリケーションサーバーに選ぶだけで、セキュリティ脆弱性、ディスク上のサイズ、インストール済みライブラリの数を減らしつつ、Pythonアプリケーションに必要な:3.10の要件も満たせます。

これで決まりですね!タグに:3.10-slimを追加すれば準備完了です!

あと一歩です!最初のルール、つまり要件を満たす小さなDockerベースイメージを選ぶことはできましたが、2つ目の課題が残っています。Pythonアプリケーションサーバーをビルドするたびに、まったく同じDockerベースイメージが使われるようにする必要があります。

その方法はいくつかあります。

  1. Docker HubからDockerベースイメージのダイジェストを取得する。

  2. docker pull python:3.10-slimを実行してDockerイメージをコンピューターにダウンロードすると、イメージダイジェストが表示されます。

3.10-slim: Pulling from library/python
7d63c13d9b9b: Pull complete
6ad2a11ca37b: Pull complete
1d79bc863ed3: Pull complete
c72b5f03bec8: Pull complete
0c3b0c5ce69b: Pull complete
Digest: sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d
Status: Downloaded newer image for python:3.10-slim
docker.io/library/python:3.10-slim

すでにPython Dockerイメージがコンピューターにある場合は、docker images --digests | grep pythonコマンドを使って、ディスク上にあるイメージのダイジェストを確認できます。

python    3.10-slim    sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d

ベースイメージのダイジェストがわかったら、前述のDockerfileに追加します。

Dockerfile

FROM python:3.10-slim@sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d
WORKDIR /usr/app
COPY . .
RUN pip install -r requirements.txt
CMD [ "python", "app.py" ]

この方法なら、PythonアプリケーションのDockerイメージを再ビルドするたびに、同じ基盤のオペレーティングシステムとライブラリのバージョンが使われます。これにより、再現性の高いビルドを実現できます。

2. 依存関係とソースコードを分離する

この2つ目のベストプラクティスは、依存関係のあるプロジェクトを含むあらゆるDockerイメージでよくあるエラーを防ぎます。まず、避けるべき方法を見てみましょう。

  1. プロジェクトフォルダー内のすべてをイメージのコンテキストにコピーする。

  2. 依存関係をインストールする。

  3. アプリケーションを実行する。

確かに動きますが、改善の余地は大いにあります。まず、ローカルでプロジェクトを開発するとき、依存関係が変わった場合にだけインストールしますよね?コードをほんの少し変更するたびに、Dockerイメージに依存関係をダウンロードしてインストールさせるのはやめましょう。

このベストプラクティスでは、Dockerイメージのレイヤーを最適化します。Dockerビルドでキャッシュシステムを活用するには、Dockerfileを書く際に、変更される可能性の高いものから順にレイヤーを並べることが重要です。

これまで使ってきたDockerfileを見てみましょう。Pythonアプリケーションのイメージをビルドするたびに、Dockerは各レイヤーを調べて、次のように判断します。何か変更されたのか、それとも以前のものをそのまま使えるのか?

現在のDockerfileでは、プロジェクトフォルダーに変更があるとCOPY命令が再実行され、その後のビルドレイヤーもすべて再実行されます。これは合理的とは言えず、最適化や高速化の余地が大いにあります。

次のDockerfileを使って改善しましょう。

FROM python:3.10-slim@sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d
WORKDIR /usr/app

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .
CMD [ "python", "app.py" ]

この新しいDockerfileでは、次回Dockerがレイヤーを再利用できるか確認したとき、requirements.txtファイルに変更がなければ、COPY命令までスキップします。これなら数秒で完了します。この小さな変更によって、ビルドプロセスを大幅に高速化できます。コードを少し変更するたびに、ビルドの間に何分も待つ必要はもうありません。

依存関係のすべてがwheelsとしてパッケージ化されているとは限らない点に注意してください。その場合、イメージにコンパイラーをインストールする必要があります。

でも、アプリケーションの実行には可能な限り小さなイメージを使うべきだと言っていましたよね!

その通りです。そこで、Dockerのもう1つの優れた機能、マルチステージビルドを紹介します。

マルチステージビルド

マルチステージビルドでは、必要な依存関係のコンパイルに多くのツールを含むDockerイメージを使い、その後、生成された成果物のうち必要なものだけを、実際に使うPython Dockerイメージにコピーします。

Node.jsベースのアプリケーションを例にすると、次のようになります。

FROM node:latest AS build
ARG NPM_TOKEN
WORKDIR /usr/src/app
COPY package*.json /usr/src/app/
RUN npm install

FROM node:lts-alpine@sha256:b2da3316acdc2bec442190a1fe10dc094e7ba4121d029cb32075ff59bb27390a
WORKDIR /usr/src/app
COPY --from=build /usr/src/app/node_modules /usr/src/app/node_modules
COPY . .
CMD ["node", "server.js"]

注:これはマルチステージビルドの機能を紹介するための簡単な例です。Node.jsアプリケーションの適切なコンテナ化に関するベストプラクティスを知りたい方は、Liran Tal(見覚えのある名前ですね……)とYoni Goldbergによるこちらの記事をご覧ください。

Node.jsのマルチステージビルドは比較的簡単です。node_modulesフォルダーがプロジェクト本体と同じフォルダー内にあるためです。しかし、Pythonアプリケーションではそうはいきません。

単にpip installを実行すると、あちこちにさまざまなものがインストールされ、マルチステージビルドを実行できなくなります。これを解決する方法は2つあります。

  1. pip install --userを使う

  2. virtualenvを使う

pip install --userを使えば、すべてのパッケージが~/.localディレクトリーにインストールされるため、あるステージから別のステージへのコピーが簡単にできそうです。しかし、別の問題が生じます。依存関係のコンパイルに使ったイメージのシステムレベルの依存関係がすべて、最終的なDockerベースイメージに追加されてしまうのです。これは避けたいところです(できる限り小さなDockerベースイメージにするというベストプラクティスを思い出してください)。

最初の方法は使えないため、2つ目のvirtualenvを使う方法を見ていきましょう。この方法を使うと、次のようなDockerfileになります。

FROM python:3.10-slim as build
RUN apt-get update
RUN apt-get install -y --no-install-recommends \
	      build-essential gcc 

WORKDIR /usr/app
RUN python -m venv /usr/app/venv
ENV PATH="/usr/app/venv/bin:$PATH"

COPY requirements.txt .
RUN pip install -r requirements.txt

FROM python:3.10-slim@sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d
WORKDIR /usr/app/venv
COPY --from=build /usr/app/venv ./venv
COPY . .

ENV PATH="/usr/app/venv/bin:$PATH"
CMD [ "python", "app.py" ]

これで、必要な依存関係をすべて揃えつつ、コンパイルに必要な追加パッケージを含めずに済みます。

Pythonアプリケーションのコンテナ化でマルチステージビルドを使う際の既知の問題

現在、前段のステージがキャッシュされないという既知の問題があります。最も簡単な解決方法は、BuildKitを使い、ビルドプロセスに引数BUILDKIT_INLINE_CACHE=1を追加することです。

最初のビルドは通常どおり行いますが、2回目以降は次のコマンドを使います。

export DOCKER_BUILDKIT=1
docker build -t flask-application --cache-from flask-application --build-arg BUILDKIT_INLINE_CACHE=1 .

DockerにビルドプロセスでBuildKitを使わせるには、環境変数DOCKER_BUILDKIT=1が必要です。また、Dockerソフトウェアの設定ファイル/etc/docker/daemon.jsonに次の設定を追加して有効にすることもできます(この場合、環境変数は不要です)。

{ "features": { "buildkit": true } }

3. 本番環境ではPython WSGIを使う

本番環境向けに構築したPythonアプリケーションでデバッグモードを有効にしたままにするのは、絶対に避けるべきことであり、セキュリティインシデントを招きかねません。残念ながら、コンテナ化されたPython Flaskアプリケーションや、その他のWSGI対応Pythonアプリケーションフレームワークに関する多くのブログ記事で、よく見かける誤りです。

デバッガーを使うと、ブラウザーから任意のPythonコードを実行できるため、セキュリティ上の非常に大きなリスクが生じます。ある程度は防ぐことができますが、脆弱性が残ることに変わりはありません。セキュリティのため、もう一度強調します。本番環境では、開発サーバーやデバッガーを実行しないでください!これは、コンテナ化したPythonアプリケーションにも当てはまります。

WSGIサーバーとWebサーバー/プロキシを設定するより、Python Flaskアプリケーションをデバッグモードでデプロイするほうが簡単なのはわかります。しかし、アプリケーションがハッキングされた理由を説明する羽目になるまでは、という話です。

では、どうすればよいのでしょうか?まず、使いたいWSGIサーバーの実装を選びましょう。Pythonアプリケーションで一般的に使われるのは、主に次の4つです。

  • Green Unicorn(Gunicorn) — RubyのUnicornプロジェクトから移植された、prefork型のワーカーモデルです。

  • uWSGI — 汎用性とパフォーマンスに優れ、リソース消費を抑えたWSGIサーバーの実装です。

  • mod_wsgi — Python WSGI仕様に対応するあらゆるWebアプリケーションをホストできるApacheモジュール。

  • CherryPy — Pythonらしいオブジェクト指向のHTTPフレームワークで、WSGIサーバーとしても機能します。

この記事では例としてGunicornを使用しますが、それぞれのドキュメントや情報もぜひご覧になり、ご自身のニーズに最適なものをお選びください。この記事では、ユースケースによって異なるため、設定については扱いません。

コンテナ化にあたって必要なのは、次の作業だけです。

  1. requirements.txtにgunicornの依存関係を追加する

  2. Pythonアプリケーションコンテナのエントリーポイントを変更する(変更にはCMD命令を使用します)。

FROM python:3.10-slim as build
RUN apt-get update
RUN apt-get install -y --no-install-recommends \
	build-essential gcc 

WORKDIR /usr/app
RUN python -m venv /usr/app/venv
ENV PATH="/usr/app/venv/bin:$PATH"

COPY requirements.txt .
RUN pip install -r requirements.txt

FROM python:3.10-slim@sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d
WORKDIR /usr/app
COPY --from=build /usr/app/venv ./venv
COPY . .

ENV PATH="/usr/app/venv/bin:$PATH"
CMD [ "gunicorn", "--bind", "0.0.0.0:5000", "manage:app" ]

この新しいDockerfileを使ってPythonアプリケーションを再ビルドしたら、Flaskアプリケーションがリクエストを処理できる状態かどうかを実行してテストできます。

docker run -p 8080:5000 flask-application

注: 本番環境向けのデプロイでは、Gunicornが公開するポートをホストに直接バインドしないことをおすすめします。代わりに、同じネットワーク内にリバースプロキシサーバーをデプロイし、すべてのHTTPリクエストを処理して静的ファイルを配信することをおすすめします。

4. コンテナ化したPythonアプリケーションは、可能な限り最小限の権限で実行する(rootでの実行は絶対に避ける)

最小権限の原則は、Unixの黎明期から長く用いられているセキュリティ対策です。コンテナ化したPythonアプリケーションを実行する際も、常にこの原則に従いましょう。

公式のpython Dockerイメージには、デフォルトで権限を持つユーザーが含まれていません。そのため、最小権限のユーザーでプロセスを実行できるよう、ユーザーを作成する必要があります。

そのために、実際にgunicornプロセスを実行する最終イメージ(マルチステージビルドの第2ステージ)のDockerfileに、次のgroupaddコマンドを追加します。

FROM python:3.10-slim@sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d

RUN groupadd -g 999 python && \
    useradd -r -u 999 -g python python
USER 999
WORKDIR /usr/app

COPY --from=build /usr/app/venv ./venv
COPY . .

ENV PATH="/usr/app/venv/bin:$PATH"
CMD [ "gunicorn", "--bind", "0.0.0.0:5000", "manage:app" ]

ただし問題があります。前述の変更を行うと、Dockerが実行するシステムプロセスの所有者はユーザーpythonになります……。コピーしたファイルやWORKDIRディレクトリの所有者はどうなるでしょうか?Dockerは、デフォルトでWORKDIRディレクトリが存在しない場合に作成しますが、その所有者はシステムユーザーのrootになります。そのため、ディレクトリへの書き込みを伴う操作を行うと、アプリケーションが致命的なエラーを起こす可能性があります。また、所有権の動作を変更しなければ、ユーザーを変更済みであっても、コピーしたファイルの所有者はデフォルトでrootになります。

修正しましょう。

FROM python:3.10-slim@sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d

RUN groupadd -g 999 python && \
    useradd -r -u 999 -g python python

RUN mkdir /usr/app && chown python:python /usr/app
WORKDIR /usr/app

COPY --chown=python:python --from=build /usr/app/venv ./venv
COPY --chown=python:python . .

USER 999

ENV PATH="/usr/app/venv/bin:$PATH"
CMD [ "gunicorn", "--bind", "0.0.0.0:5000", "manage:app" ]

上記のようにCOPY命令を更新すると、ファイルの所有権に起因するWORKDIRディレクトリでの予期しない動作を防ぎ、すべてのファイルがプロセスを実行するユーザーと同じユーザーに所有されるようにできます。

5. コンテナ化したPythonアプリケーションの異常状態に対処する

アプリケーションをデプロイする際は、未処理のイベントや問題によってアプリケーションが異常な状態になる可能性を考慮する必要があります。この状態では、1)アプリケーションが動作しなくなっても、2)プロセスは終了しません。この場合、コンテナには通知が届かず、実行中のPythonアプリケーションサーバーがHTTPリクエストに応答しなくなります。

これを防ぐには、ヘルスチェック用のエンドポイントを実装します。コンテナ化したPythonアプリケーションの健全性を確認するには、アプリケーションがユーザーのリクエストを引き続き正常に処理できることを確認するため、monitoringまたはhealthのHTTPエンドポイントを設けることをおすすめします。FlaskなどのPython用Webアプリケーションフレームワークでは、簡単に実装できます(以下のFlaskの例を参照)。DockerのHEALTHCHECK命令と組み合わせることで、アプリケーションの健全性を適切に監視できます。

以下は、/health HTTPエンドポイントを追加するPython Flaskアプリケーションのコード例です。

@app.route('/health', methods=['GET'])
def health():
	# Handle here any business logic for ensuring you're application is healthy (DB connections, etc...)
    return "Healthy: OK"

このエンドポイントをアプリケーションに実装したら、DockerfileにHEALTHCHECK命令を追加するだけです。

FROM python:3.10-slim@sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d

RUN groupadd -g 999 python && \
    useradd -r -u 999 -g python python

RUN mkdir /usr/app && chown python:python /usr/app
WORKDIR /usr/app

COPY --chown=python:python --from=build /usr/app/venv ./venv
COPY --chown=python:python . .

USER 999

ENV PATH="/usr/app/venv/bin:$PATH"
CMD [ "gunicorn", "--bind", "0.0.0.0:5000", "manage:app" ]
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD curl -f http://localhost:5000/health

Kubernetesにデプロイする場合、DockerfileのHEALTHCHECKディレクティブは無視される点に注意してください。代わりに、YAMLにKubernetesのliveness、readiness、startupプローブを設定する必要があります。

そのため、上記のHEALTHCHECK命令をKubernetesへのデプロイで使用する場合は、次のように対応します。

...
   livenessProbe:
     httpGet:
       path: /health
       port: 5000
     initialDelaySeconds: 5
     periodSeconds: 30
     timeoutSeconds: 30
     failureThreshold: 3
...

この設定は、PodまたはデプロイメントのYAMLファイルにあるcontainer specの部分にネストされます。alwaysまたはunless_stoppedの再起動ポリシーを設定すると、Pythonアプリケーションコンテナが異常な状態になった場合に必ず再起動されます。

6. Python Dockerアプリケーションイメージのセキュリティ脆弱性を見つけて修正する

すでに見てきたように、Dockerのベースイメージが大きいと、保守やセキュリティ修正の適用、最新状態の維持が必要なソフトウェアスタックが増えるなど、さまざまな問題が生じます。

さまざまなベースイメージのサイズや脆弱性の指標を確認するために、Snyk Advisorを使う方法も紹介しました。しかし、Advisorはほんの入り口にすぎません。Snykは無料の開発者向けセキュリティプラットフォームです。PythonコードやPythonの依存関係(requirements.txt内のものなど)、アプリケーションを実行するPythonコンテナイメージ、さらにそれらをオーケストレーションするTerraformやKubernetesの設定まで、あらゆるものをテストできます。

Snykの優れた点は、検出した脆弱性に対する修正案を提示することです。セキュリティ脆弱性の存在を知らせるだけでなく、修正用のPRを自動で作成したり、修復方法を提案したりします。しかも、既存のツール(IDE、CLI、Dockerなど)やワークフロー(Git、CI/CDなど)内で、すべてを実行できます。

例:Snykでコンテナ化したPythonアプリをスキャンする

このサンプルPython Flaskアプリケーションのビルドにpython:3.8のPython Dockerイメージを使用した場合、Snyk CLIでどのようにスキャンできるか見てみましょう。

Snyk CLIをインストールすると、Pythonプロジェクトの依存関係やPythonコードなどをスキャンできます。まず、Snyk CLIをインストールしましょう。Node.js環境がある場合は、次のようにnpmパッケージマネージャーを使ってインストールできます。

npm install -g snyk

macOSまたはLinuxでHomebrewを使用している場合は、次のようにインストールできます。

brew tap snyk/tap
brew install snyk

その他のインストール方法については、Snyk CLIのインストール方法をご覧ください。

次に、脆弱性データベースを照会するための有効なAPIトークンを取得するため、CLIで認証します。

snyk auth

ここまで完了したら、python:3.8ベースイメージを使ってPythonアプリケーションのDockerイメージをローカルにビルドします。

❯ docker build . -t python-flask-app
FROM python:3.8 as build
[+] Building 5.2s (8/13)
 => [internal] load build definition from Dockerfile
 => => transferring dockerfile: 513B
 => [internal] load .dockerignore
 => => transferring context: 2B
 => [internal] load metadata for docker.io/library/python:3.8
 => [auth] library/python:pull token for registry-1.docker.io
 => [internal] load build context
...

続いて、次のコマンドを実行してSnykでスキャンしましょう。

snyk container test python-flask-app

実行すると、次の結果が表示されます(出力が長いため、意図的に短縮しています)。

Testing python-flask-app...

✗ Low severity vulnerability found in tiff/libtiff5
  Description: Out-of-bounds Read
  Info: https://snyk.io/vuln/SNYK-DEBIAN11-TIFF-514595
  Introduced through: imagemagick@8:6.9.11.60+dfsg-1.3, imagemagick/libmagickcore-dev@8:6.9.11.60+dfsg-1.3
  From: imagemagick@8:6.9.11.60+dfsg-1.3 > imagemagick/imagemagick-6.q16@8:6.9.11.60+dfsg-1.3 > imagemagick/libmagickcore-6.q16-6@8:6.9.11.60+dfsg-1.3 > tiff/libtiff5@4.2.0-1
  From: imagemagick/libmagickcore-dev@8:6.9.11.60+dfsg-1.3 > imagemagick/libmagickcore-6.q16-dev@8:6.9.11.60+dfsg-1.3 > tiff/libtiff-dev@4.2.0-1 > tiff/libtiff5@4.2.0-1
  From: imagemagick/libmagickcore-dev@8:6.9.11.60+dfsg-1.3 > imagemagick/libmagickcore-6.q16-dev@8:6.9.11.60+dfsg-1.3 > tiff/libtiff-dev@4.2.0-1 > tiff/libtiffxx5@4.2.0-1 > tiff/libtiff5@4.2.0-1
  and 3 more...

✗ High severity vulnerability found in imagemagick/imagemagick-6-common
  Description: Information Exposure
  Info: https://snyk.io/vuln/SNYK-DEBIAN11-IMAGEMAGICK-1246513
  Introduced through: imagemagick/libmagickcore-dev@8:6.9.11.60+dfsg-1.3, imagemagick/libmagickwand-dev@8:6.9.11.60+dfsg-1.3, imagemagick@8:6.9.11.60+dfsg-1.3
  From: imagemagick/libmagickcore-dev@8:6.9.11.60+dfsg-1.3 > imagemagick/imagemagick-6-common@8:6.9.11.60+dfsg-1.3
  From: imagemagick/libmagickwand-dev@8:6.9.11.60+dfsg-1.3 > imagemagick/imagemagick-6-common@8:6.9.11.60+dfsg-1.3
  From: imagemagick@8:6.9.11.60+dfsg-1.3 > imagemagick/imagemagick-6.q16@8:6.9.11.60+dfsg-1.3 > imagemagick/libmagickcore-6.q16-6@8:6.9.11.60+dfsg-1.3 > imagemagick/imagemagick-6-common@8:6.9.11.60+dfsg-1.3
  and 24 more...

✗ Critical severity vulnerability found in python3.9/libpython3.9-stdlib
  Description: Improper Input Validation
  Info: https://snyk.io/vuln/SNYK-DEBIAN11-PYTHON39-1290158
  Introduced through: mercurial@5.6.1-4
  From: mercurial@5.6.1-4 > python3-defaults/python3@3.9.2-3 > python3-defaults/libpython3-stdlib@3.9.2-3 > python3.9/libpython3.9-stdlib@3.9.2-1
  From: mercurial@5.6.1-4 > python3-defaults/python3@3.9.2-3 > python3.9@3.9.2-1 > python3.9/libpython3.9-stdlib@3.9.2-1
  From: mercurial@5.6.1-4 > python3-defaults/python3@3.9.2-3 > python3-defaults/python3-minimal@3.9.2-3 > python3.9/python3.9-minimal@3.9.2-1
  and 4 more...

✗ Critical severity vulnerability found in glibc/libc-bin
  Description: Use After Free
  Info: https://snyk.io/vuln/SNYK-DEBIAN11-GLIBC-1296898
  Introduced through: glibc/libc-bin@2.31-13+deb11u2, meta-common-packages@meta
  From: glibc/libc-bin@2.31-13+deb11u2
  From: meta-common-packages@meta > glibc/libc-dev-bin@2.31-13+deb11u2
  From: meta-common-packages@meta > glibc/libc6@2.31-13+deb11u2
  and 1 more...

Organization:      snyk-demo-567
Package manager:   deb
Project name:      docker-image|python-flask-app
Docker image:      python-flask-app
Platform:          linux/amd64
Base image:        python:3.8.12-bullseye
Licenses:          enabled

Tested 427 dependencies for known issues, found 171 issues.

Base Image              Vulnerabilities  Severity
python:3.8.12-bullseye  171              6 critical, 6 high, 27 medium, 132 low

Recommendations for base image upgrade:

Alternative image types
Base Image                   Vulnerabilities  Severity
python:3.9-slim              37               1 critical, 0 high, 1 medium, 35 low
python:3.11-rc-slim          37               1 critical, 0 high, 1 medium, 35 low
python:3.8.12-slim-bullseye  37               1 critical, 0 high, 1 medium, 35 low
python:3.10-slim-buster      70               2 critical, 9 high, 9 medium, 50 low

このPython 3.8オペレーティングシステムの内容には、オープンソースライブラリとして427個の依存関係が含まれています。ベースイメージにpython:3.8を選んだため、このPython Flaskアプリケーションには合計171件のセキュリティ脆弱性が持ち込まれています。

ここで「どうすれば修正できるのだろう?」と疑問に思うかもしれません。幸い、Snykは攻撃対象領域を縮小するために、アップグレードまたは完全に切り替えられる別のベースイメージを提案します。

ベースイメージの推奨内容を視覚的にわかりやすく示したスクリーンショットをご覧ください。

ターミナルの出力に、python:3.8.12-bullseyeで171件の脆弱性が報告され、代替のPythonベースイメージと脆弱性の件数が比較されています。

これで、データに基づいてPythonアプリケーションのセキュリティを強化する判断ができます。Snykが推奨する代替Dockerイメージを選ぶことで、アプリケーションに含まれるソフトウェアの攻撃対象領域を大幅に縮小できます。

アプリケーションのセキュリティをさらに管理するには、リポジトリをSnyk UIに接続してソースコードとDockerfileをインポートしましょう。脆弱性を検出できるだけでなく、新たなセキュリティ問題がないか継続的に監視できます。以下は、同じDockerベースイメージのレポートをSnyk UIで表示したものです。

Debian 11上のPython 3.8を示すSnyk Containerイメージの詳細と、脆弱性数を踏まえたベースイメージのアップグレードに関する推奨事項。

セキュリティ脆弱性を監視して見つけるだけでなく、修正までできたら最高ですよね!:-)

GitリポジトリをSnykに接続すると、ここに示すように、Dockerベースイメージのアップグレードを提案するプルリクエストをリポジトリに自動で作成することもできます。

脆弱性に対処するため、node:10からnode:debian-buster-slimへのカート/Dockerfileの更新を示すGitHubプルリクエスト

興味があれば、Dockerfileのプルリクエストでコンテナセキュリティを自動化する方法を紹介した続編もご覧ください。

Pythonアプリケーションをコンテナ化するにはどうすればよいですか?

Dockerは、PythonのDockerイメージをベースにしたコンテナ化済みのPythonアプリケーションとして、再利用可能でクロスプラットフォームに対応し、すばやくデプロイできるソフトウェアを構築できる仮想化技術です。これらのアプリケーションは、Dockerfileというファイルを使い、Infrastructure as Codeとして定義します。

コンテナ化されたPythonアプリケーションをビルドして使用するには、次のコマンドを実行します。

docker build -t flask-application .
docker run -p 8080:5000 flask-application

開発者向けPythonセキュリティの推奨事項

こうしたベストプラクティスは、コンテナ化したPythonアプリの作成、管理、保護に役立ちます。この記事を楽しんでいただき、アプリケーションセキュリティやセキュリティ推進に関心をお持ちの方には、次の資料もおすすめします。

開発者ファーストのコンテナセキュリティ

Snykは、コンテナイメージとKubernetesワークロードの脆弱性を検出し、自動的に修正します。

続きを読む

feature customer snowflake
Article

開発初期からセキュリティを確保:Snowflake Cortex Code向けSnyk Studioインテグレーションを発表

Snyk StudioがSnowflake Cortex Codeと連携し、開発中にAI生成コード、依存関係、コンテナの脆弱性をスキャンします。

Blog

Stadium Summer:Snyk Connect Fan Zone Tour

SnykのFan Zone Tourでは、8都市と3回のオンラインセッションで、AIセキュリティのワークショップやネットワーキング、楽しい競技を開催しました。参加者はスキルを磨き、アイデアを共有し、共に成長しました。

Blog

Snyk VulnBench JS 1.0:LLMは同じバグを2回見つけられるか?

Snyk VulnBench JS 1.0:300回の反復スキャンで、LLMのセキュリティ検出結果は実行ごとに異なる一方、SASTとモデルはそれぞれ異なる脆弱性の見落としを検出することが明らかに。