Dockerにおける環境変数のマスター:設定とシークレットの使い分け
Dockerは、アプリケーションのビルド、出荷、実行の方法に革命をもたらし、開発のさまざまなステージで一貫した環境を提供します。コンテナ化されたアプリケーションを管理する上で基本的な側面は、それらの設定であり、環境変数はそのための主要なメカニズムです。しかし、すべてのデータが同じように扱われるわけではありません。一部の設定は無害ですが、APIキーやデータベースの認証情報などの他の情報は非常に機密性が高いです。これら2つのカテゴリを混同すると、重大なセキュリティ脆弱性につながる可能性があります。
この記事では、一般的な設定に環境変数を使用することと、一般的に「シークレット」と呼ばれる機密データを管理するための適切で安全な方法との重要な違いを探ります。Dockerコンテナに設定を渡すさまざまな方法、秘密情報を通常の環境変数として扱うことの固有のリスクを強調し、安全なシークレット管理のためのDocker専用ソリューションを紹介します。最後までには、それぞれの方法をいつ、どのように使用するかについて明確に理解し、アプリケーションの柔軟性とセキュリティの両方を確保できるようになります。
設定のための環境変数の理解
環境変数は、Dockerコンテナで実行されているものを含むアプリケーションに、実行時の設定を渡すための直接的で広く採用されている方法です。これにより、Dockerイメージを再ビルドすることなくアプリケーションの動作を変更できるため、コンテナの柔軟性と移植性が向上します。これは、アプリケーションのポート番号、デバッグフラグ、サードパーティサービスのURLなど、機密性のない動的な設定に最適です。
設定変数を渡す方法
Dockerは、コンテナ内に環境変数を定義して注入するためのいくつかの方法を提供しています。
1. Dockerfile内のENV命令
ENV命令は、コンテナが実行されるときにコンテナ内で利用可能になるデフォルトの環境変数を設定します。これは、変更される可能性が低い変数、またはアプリケーションの適切なデフォルト値を提供するのに適しています。
FROM alpine:latest
ENV APP_PORT=8080
ENV DEBUG_MODE=false
COPY ./app /app
WORKDIR /app
CMD ["/app/start.sh"]
ヒント: ENVはデフォルトを設定しますが、これらは実行時に上書きできます。
2. docker runの-eまたは--envフラグ
単一のコンテナを起動するときに、-eまたは--envフラグを使用して環境変数を直接渡すことができます。これは、アドホックなテストや、Dockerfileのデフォルトとは異なる特定の値を指定するためによく使用されます。
docker run -d -p 80:8080 --name my_app_instance \n -e APP_PORT=80 \n -e DEBUG_MODE=true \n my_app_image:latest
3. Docker Composeのenv_file
特にdocker-compose.ymlファイルで定義された複数のサービスにわたる複数の環境変数を管理する場合、env_fileオプションは非常に便利です。これにより、1つ以上の.envファイルから変数を読み込むことができ、docker-compose.ymlをよりきれいに保つことができます。
docker-compose.yml:
version: '3.8'
services:
webapp:
image: my_app_image:latest
ports:
- "80:8080"
env_file:
- ./config/app.env
./config/app.env:
APP_PORT=8080
DEBUG_MODE=false
API_ENDPOINT=https://api.example.com/v1
4. Docker Composeのenvironmentキー
または、docker-compose.ymlのサービスのenvironmentセクション内で環境変数を直接定義することもできます。これは、少数の変数、または単一のサービスに固有の変数に対して好まれることがよくあります。
version: '3.8'
services:
webapp:
image: my_app_image:latest
ports:
- "80:8080"
environment:
APP_PORT: 8080
DEBUG_MODE: false
環境変数をシークレットに使用する落とし穴
環境変数は設定には優れていますが、データベースのパスワード、APIキー、秘密のSSHキーなどの機密データを管理するためには、基本的に安全ではありません。これは、特に開発環境でしばしば見落とされる重大なセキュリティ脆弱性です。
環境変数がシークレットにとって安全でない理由:
-
docker inspectによる可視性: Dockerホストへのアクセス権を持つ誰もが、docker inspect <container_id>を使用して実行中のコンテナの環境変数を簡単に見ることができます。これは、シークレットがプレーンテキストで明確に見えることを意味します。```bash
シークレットを公開する例(本番環境では絶対に行わないでください)
docker run -d -e DB_PASSWORD=mysecretpassword --name insecure_app nginx:latest
誰もがパスワードを見ることができます
docker inspect insecure_app | grep DB_PASSWORD
``` -
プロセスののぞき見: コンテナ内では、アプリケーションがrootまたは昇格された権限で実行されている場合、他のプロセスやユーザー(複数のユーザーが存在する場合)が環境変数を読み取れる可能性があります。
-
ロギングと履歴: 環境変数は、意図せずログ、CI/CDパイプラインの履歴、またはシェル履歴に紛れ込み、偶発的な漏洩につながる可能性があります。
-
イメージレイヤー: Dockerfileでシークレットと共に
ENVを使用すると、そのシークレットはイメージレイヤーに焼き付けられ、後続のレイヤーでunsetしようとしても、そこにとどまります。これにより、イメージ自体からシークレットを取得できるようになります。 -
偶発的な共有: シークレットを含む
.envファイルやdocker-compose.ymlファイルは、バージョン管理システムにコミットされたり、不適切に共有されたりすることが多く、広範な漏洩につながります。
警告: 機密情報を通常の環境変数として扱うことは、よくあるセキュリティ上の誤りです。環境変数はホスト上およびコンテナ内で公開される可能性があると常に想定してください。
Dockerでの安全なシークレット管理
機密データに対する環境変数のセキュリティ上の不備に対処するため、Dockerは専用のシークレット管理機能を提供しています。これには、主にDocker Secrets(Docker Swarm用)と、ファイルのマウントを伴う(Docker Swarmのシークレットを利用する場合もある)シークレット機能付きDocker Composeなどの外部ツールが含まれます。
Docker Secrets(Docker Swarmモード)
Docker Secretsは、Docker Swarmモードに統合された機能で、サービスに対して機密データを安全に送信および保存する方法を提供します。シークレットは次のとおりです。
- SwarmマネージャーのRaftログでアットレスト(保管時)に暗号化されます。
- 承認されたサービスタスクに安全に送信されます。
- 環境変数として公開されるのではなく、コンテナのファイルシステム内のインメモリファイルとしてマウントされます(通常は
/run/secrets/<secret_name>)。 - アクセスを明示的に許可されたサービスのみがアクセス可能です。
Docker Secrets(Swarmモード)の使用方法
-
Swarmの初期化(まだの場合):
bash docker swarm init -
シークレットの作成: シークレットは、ファイルまたは標準入力から作成されます。
bash echo "my_secure_db_password" | docker secret create db_password_secret - echo "SG.your_api_key_here" | docker secret create sendgrid_api_key - -
シークレットを使用したサービスのデプロイ: サービスは名前でシークレットを参照します。Dockerはシークレットをコンテナ内にマウントします。
bash docker service create --name my-webapp \n --secret db_password_secret \n --secret sendgrid_api_key \n my_app_image:latest -
コンテナ内でのシークレットへのアクセス: アプリケーションはマウントされたファイルパスからシークレットを読み取ります。
```python
Pythonアプリケーションコード内(他の言語でも同様)
with open('/run/secrets/db_password_secret', 'r') as f:
db_password = f.read().strip()
with open('/run/secrets/sendgrid_api_key', 'r') as f:
sendgrid_key = f.read().strip()
```
Docker Composeとシークレット(単一ホストまたはSwarm用)
Docker Composeバージョン3.1以降では、secretsセクションが導入され、docker-compose.yml内でシークレットを定義および参照できるようになりました。Swarmモードで実行される場合、ComposeはDocker Swarmのネイティブシークレットを活用します。Swarmモードなしで単一ホストで実行される場合でも、Composeはホストからコンテナにファイルを安全にマウントすることでシークレットをサポートしますが、Swarmが提供するアットレストでの暗号化はありません。
docker-compose.ymlでのsecretsの使用
-
シークレットの定義: 外部ファイルを参照するか、外部シークレット(事前に作成されたSwarmシークレット)にすることで、シークレットを定義できます。
```yaml
docker-compose.yml
version: '3.8'
services:
webapp:
image: my_app_image:latest
ports:
- "80:8080"
secrets:
- db_password
- sendgrid_api_keysecrets:
db_password:
file: ./secrets/db_password.txt # パスワードを含むホスト上のファイルへのパス
sendgrid_api_key:
external: true # 'sendgrid_api_key'という名前の既存のDocker Swarmシークレットを参照
``` -
ローカルシークレットファイルの作成(
fileを使用する場合):
bash mkdir secrets echo "my_local_db_password" > ./secrets/db_password.txt -
Composeを使用したデプロイ:
docker compose up -dを実行するとサービスがデプロイされ、シークレットはコンテナ内で/run/secrets/<secret_name>で利用可能になります。```bash
コンテナ内では、./secrets/db_password.txtの内容は次の場所にあります。
/run/secrets/db_password
```
適切なツールの選択:設定 対 シークレット
環境変数を使用するか、専用のシークレット管理ソリューションを使用するかを決定する際の鍵は、次の1つの主要な質問に集約されます。
そのデータは機密情報ですか?
- はい(機密データの場合): Docker Secrets(Swarm使用時)または同様のシークレット管理システム(例:Kubernetes Secrets、HashiCorp Vault)を使用します。単一ホストのComposeセットアップの場合は、
secretsセクションを使用してファイルを安全にマウントします。 - いいえ(機密性のない設定の場合): 環境変数(Dockerfileの
ENV、-eフラグ、env_file、またはComposeのenvironment経由)を使用します。
| 機能 | 環境変数(設定用) | Docker Secrets(機密データ用) |
|---|---|---|
| 目的 | 機密性のないアプリケーション設定 | 機密データ(パスワード、APIキー) |
| 可視性 | docker inspect、ps -eで表示可能 |
ファイルとしてマウントされ、docker inspectには表示されない |
| セキュリティ | 機密データには安全ではない | 暗号化され、安全な送信と保存 |
| アプリでのアクセス | os.environなどから読み取る |
/run/secrets/<secret_name>ファイルから読み取る |
| 管理元 | Dockerランタイム、Docker Compose | Docker Swarm、Docker Compose |
| ユースケース | ポート番号、デバッグフラグ、非機密URL | データベースパスワード、APIトークン、秘密鍵 |
両方のベストプラクティス
設定(環境変数)について:
- Dockerfileで
ENVを使用して適切なデフォルト値を提供します。これにより、イメージはすぐに実行可能になり、期待される変数が明確に文書化されます。 - 可能な限り設定を外部化します。
docker composeで.envファイルを使用するか、大規模デプロイでは外部設定サービスを使用します。 - すべての設定オプションとその期待される値を、
README.mdやアプリケーションのドキュメントなどで文書化します。 - 環境(開発、ステージング、本番)間で変更される可能性のある値をハードコーディングしないようにします。
シークレット(Docker Secretsなど)について:
- シークレット(例:秘密を含む
.envファイル、db_password.txt)をGitなどのバージョン管理システムにコミットしない。 - シークレットを定期的にローテーションします。これにより、シークレットが侵害された場合の露出ウィンドウが最小限に抑えられます。
- 最小特権を付与します。サービスが必要とするシークレットのみにアクセスを許可します。
- シークレット値をログに記録しないようにします。アプリケーションとインフラストラクチャのロギングでシークレットの内容が出力されないように徹底します。
- 大規模なエンタープライズグレードのデプロイメントの場合、監査、動的シークレット生成、IDおよびアクセス管理(IAM)との統合など、より高度な機能を提供するHashiCorp Vault、AWS Secrets Manager、Azure Key Vaultなどの専用シークレット管理ソリューションを検討してください。
結論
Dockerにおける環境変数のマスターとは、単にそれらを渡す方法を知っているだけでなく、汎用的な設定と機密性の高いシークレットとの根本的な違いを理解することを意味します。環境変数はアプリケーション設定に比類のない柔軟性を提供しますが、機密データにとっては本質的に安全ではありません。
Swarm環境内で機密情報にDocker Secretsを活用したり、単一ホストのデプロイメントでDocker Composeのsecrets機能を注意深く使用したりすることで、コンテナ化されたアプリケーションのセキュリティ体制を大幅に強化できます。常に適切なツールを適切な目的に使用し、ベストプラクティスを順守し、機密データが偶発的な漏洩から保護されるようにすることで、セキュリティを優先してください。この規律あるアプローチは、より堅牢で保守性が高く、安全なDockerデプロイメントにつながります。