TT Lab
はじめる
学ぶ 学習パス コース

コンテナセキュリティ

環境変数とファイルマウント、何が違うのか

TT Labで続きを見る

このラボは本物のVM上で動きます

この箱はPodではなく、KubeVirtが起動した仮想マシンです。Linuxカーネルが 別に動き、systemdが実際にサービスを管理し、dockerは偽物ではなく 本物のDockerエンジンです。docker runで起動したコンテナは、実際にプロセスに なり、docker execもdocker logsもそのまま動作します。

以前は、このラボはPodの中で動いていました。カーネルの権限をすべて下ろした箱だったため、 コンテナを起動するステップが塞がれており、そのためイメージのアーカイブを自分で展開して みるという回り道で学んでいました。もう回り道は必要ありません。

知っておくべきことが2つあります。

目標

同じシークレットを、環境変数とファイルのマウントの2つの方式で渡してみて、docker inspectへの 露出の有無と、再起動なしでのローテーションの可否を自分で測り、比較表にまとめます。 最後に、簡単なシークレット検知のスクリプトを書きます。

なぜ重要なのか

環境変数は、保管場所ではなく受け渡しの方式です。/proc/<pid>/environは、プロセスが 生きている間は読み取れ続け、コンテナの環境変数は、デーモンにアクセスできる誰にでも docker inspectでそのまま見えます。そのため、「.envを読み込んで削除したから大丈夫」という 安心には、根拠がありません。さらに重要なのは、ローテーションです。環境変数は、プロセスの開始 時点で固定されるため、値を変えるには必ず再起動が必要で、ファイルのマウントは、同じ inodeを上書きすれば、再起動なしで新しい値が見えます。この違いが、インシデント対応の時間を 分けます。認証情報が公開されたと仮定したとき、取り消して交換し、サービスを正常化する のに何分かかるかが、そのチームのシークレット管理のレベルそのものです。

ステップ

  1. /root/sec3ディレクトリを作成し、/root/sec3/app.envにAPI_KEY=labhub-env-key-v1の1行を書いたあと、ファイルの権限を600に変更します。
  2. alpine:3.20でsec3-envコンテナを、--env-file /root/sec3/app.envを使ってバックグラウンド実行します。コンテナのAPI_KEYが、ファイルの値と同じである必要があります。
  3. docker inspect sec3-envで読み取ったAPI_KEYの値を、そのまま/root/sec3/leak-proof.txtに保存します。
  4. /root/sec3/secret.txtにlabhub-file-key-v1を書き、そのファイルをsec3-fileコンテナの/run/secrets/api_keyのパスに読み取り専用でバインドマウントして、バックグラウンド実行します。マウント元は、正確に/root/sec3/secret.txtである必要があります。
  5. sec3-fileコンテナの環境変数に、API_KEYも、シークレットの値そのものも、入っていない必要があります。
  6. コンテナを再起動しないまま、/root/sec3/secret.txtの内容をlabhub-rotated-v2で上書きします。sec3-fileは新しい値を見ることになり、sec3-envの環境変数の値は、古い値のままである必要があります。そして/root/sec3/rotate.mdに、どちらの方式が再起動を必要とするかの結論を書きます(재시작またはrestartという語が入っている必要があります。前者は、韓国語で「再起動」を意味する語です)。
  7. 実行権限のある/root/sec3/find-secrets.shを作成します。第1引数で受け取ったディレクトリを調べて、AKIAで始まるアクセスキーやPRIVATE KEYブロックがあれば、0以外の終了コードで終了しつつ、どのファイルで見つけたかパスを出力し、何もなければ、終了コード0で終了する必要があります。
  8. /root/sec3/secrets.mdに、環境変数方式とファイルのマウント方式を比較する内容を書き、次の4行を正確にこの形式で含めます。
    • env_visible_in_inspect=yes
    • mount_visible_in_inspect=no
    • env_needs_restart=yes
    • mount_needs_restart=no

参考

シークレットのファイルと権限600

/root/sec3ディレクトリを作成し、/root/sec3/app.envにAPI_KEY=labhub-env-key-v1の1行を書いたあと、ファイルの権限を600に変更してください。

値そのものより、ファイルの権限が採点の対象です。所有者だけが読み書きでき、グループとその他のユーザーには、何の権限もない必要があります。ファイルを作成してから権限を変更する順序で行ってください。

env-fileで注入する

alpine:3.20でsec3-envコンテナを、--env-file /root/sec3/app.envを使ってバックグラウンド実行してください。コンテナのAPI_KEYが、ファイルの値と同じである必要があります。

値をコマンドラインに直接書くと、シェルの履歴とプロセスの一覧に残ります。ファイルから読み込んで注入するオプションを使ってください。採点がコンテナの環境変数の値とファイルの値を照合するので、コンテナは起動している必要があります。

環境変数はinspectにそのまま見える

docker inspect sec3-envで読み取ったAPI_KEYの値を、そのまま/root/sec3/leak-proof.txtに保存してください。

コンテナを作成した人でなくても、デーモンにアクセスできれば、この値を読めます。推測で書かず、実際にdocker inspectで取り出した値を、そのままファイルに残してください。

ファイルとしてマウントする

/root/sec3/secret.txtにlabhub-file-key-v1を書き、そのファイルをsec3-fileコンテナの/run/secrets/api_keyのパスに読み取り専用でバインドマウントして、バックグラウンド実行してください。マウント元は、正確に/root/sec3/secret.txtである必要があります。

ホストのファイル1つを、コンテナの特定のパスにそのまま付ける方式です。マウント元のパスと対象のパスが採点の対象なので、正確に合わせる必要があり、シークレットなので読み取り専用で付ける必要があります。

環境変数には残さない

sec3-fileコンテナの環境変数に、API_KEYも、シークレットの値そのものも、入っていない必要があります。

ファイルのマウントに変えたなら、同じ値を環境変数でも渡す理由はありません。両方与えると、ファイルのマウントの利点がなくなります。inspectにまた見えるからです。ステップ4のコンテナに、環境変数が混ざっていないかを確認してください。

再起動せずにローテーションする

コンテナを再起動しないまま、/root/sec3/secret.txtの内容をlabhub-rotated-v2で上書きしてください。sec3-fileは新しい値を見ることになり、sec3-envの環境変数の値は、古い値のままである必要があります。そして/root/sec3/rotate.mdに、どちらの方式が再起動を必要とするかの結論を書きます(재시작またはrestartという語が入っている必要があります。前者は、韓国語で「再起動」を意味する語です)。

バインドマウントはinodeを追いかけます。ファイルをその場で上書きすれば(リダイレクト)、コンテナが新しい値をすぐに見ますが、ファイルを削除して作り直す方式(一部のエディターやsed -i)は、inodeが変わって、マウントが古いファイルを指し続けます。そして、環境変数の側は、絶対に変わってはいけません。それが、このステップの対照群です。

シークレット検知のスクリプト

実行権限のある/root/sec3/find-secrets.shを作成してください。第1引数で受け取ったディレクトリを調べて、AKIAで始まるアクセスキーやPRIVATE KEYブロックがあれば、0以外の終了コードで終了しつつ、どのファイルで見つけたかパスを出力し、何もなければ、終了コード0で終了する必要があります。

ディレクトリを再帰的に走査して、2つの形を探します。AKIAで始まるアクセスキーと、PRIVATE KEYブロックのヘッダーです。きれいなら0、見つければ0以外の値で終了する必要があり、どのファイルかパスを出力する必要があります。このようなルールが、形がはっきりした値にしかうまく効かないという限界も、あわせて考えてみてください。

受け渡し方式の比較表

/root/sec3/secrets.mdに、環境変数方式とファイルのマウント方式を比較する内容を書き、次の4行を正確にこの形式で含めてください。

4行の値は、前のステップで自分で確認した結果と一致している必要があります。ステップ3で何が見えたか、ステップ5で何が見えなかったか、ステップ6でどちらが再起動を要求したかを振り返って書いてください。そして、本文には、環境変数方式とマウント方式の両方が言及されている必要があります。