メインコンテンツにスキップ ナビゲーションにスキップ フッターにスキップ
期間限定:デザインパートナープログラム。BUSINESSプランはずっと無料

シークレットの管理

デプロイ時の認証情報をフォークが届かない場所に保管します。設計上、書き込み専用です。

シークレットの管理

フォークについて言えば、暗号化されたイメージのバイト単位のコピーです。認証情報も含めてそのままです。Stripe のライブキー、データベースパスワード、API トークンがリポジトリに入っていますか?フォークはそれらをすべて引き継ぎます。サンドボックスが実際の顧客カードに課金することになります。

正しい置き場所は rdc repo secret です。2つの配信モード、設計上の書き込み専用、そしてフォークは何も持たない状態で起動します。このチュートリアルでは両方の種類を設定し、それを使うアプリをデプロイし、値が実際にコンテナに届くことを証明し、そしてシークレットが追ってこないためフォークが起動できない様子を確認します。

チュートリアル動画

落とし穴: リポジトリ内の .env

リポジトリイメージ内の .env ファイルはすべてのフォークにクローンされる

ほとんどのチームは .env をリポジトリに置きます。当然の選択です。

そしてフォークします。

フォークは親イメージのバイト単位のコピーです。.env の内容はフォークの .env にも入ります。フォークのコンテナが起動します。同じ Stripe キーを読み込みます。本番の認証情報で同じ Stripe API を呼び出します。Stripe 側から見ると、その呼び出しは あなた自身 のものです。

これは大変な目に遭うことになります。経験済みです。

ステップ1: env モードでシークレットを設定する

rdc repo secret set --name my-app --key DB_HOST --value postgres.internal --mode env

まず、envモードでシークレットを設定します。値はコンテナ内に環境変数として渡されます。初回の書き込みは手続き不要ですが、既存のシークレットを上書きする場合のみ確認が必要です。

--mode env は値をコンテナ内の環境変数として設定します。初回の書き込みには手続きは不要ですが、既存のシークレットを上書きする場合は現在の値の提示が必要です。

ステップ2: file モードでシークレットを設定する

rdc repo secret set --name my-app --key STRIPE_KEY --value sk_test_xxx --mode file

ファイルモードのシークレットを設定します。ファイルモードはコンテナの環境変数を通じて値を公開しません。Docker標準のシークレット機構を使い、/run/secrets 以下のファイルに値を書き込みます。センシティブな情報にはファイルモードを使用してください。

file モードは値をコンテナの環境変数に入れません。代わりに Docker の標準メカニズムを使って /run/secrets/stripe_key に書き込みます。機密性の高いものにはこちらを使ってください。

ステップ3: 一覧を確認する

rdc repo secret list --name my-app

設定内容を一覧表示してみましょう。表示されるのは名前とモードのみです。誰が要求しても、値は表示されません。

名前とモードだけが表示されます。値は表示されません。 リストは誰が聞いても決して値を表示しません。

compose に組み込む

docker-compose.yml を開きます。両方のモードを参照します。

services:
  api:
    image: myapp:latest
    environment:
      DATABASE_HOST: ${REDIACC_SECRET_DB_HOST}
    secrets:
      - stripe_key

secrets:
  stripe_key:
    file: /var/run/rediacc/secrets/${REDIACC_NETWORK_ID}/STRIPE_KEY

${REDIACC_SECRET_DB_HOST}env モードです。renet の compose ラッパーがデプロイ時にシークレットストアから展開します。

secrets: ブロックは file モードで、Docker の標準メカニズムを使います。ホストパスは ${REDIACC_NETWORK_ID} を使っているため、同じ compose が親リポジトリとフォークで使えます。各フォークは独自のネットワーク ID を持ちます。

値は読み返せない

書き込み専用モデル: get はダイジェストを返し、値は返さない

ここが最初のうちは多くの人を困らせる部分です。私も含めて。

ステップ4: get はダイジェストを返す

rdc repo secret get --name my-app --key STRIPE_KEY

secret get コマンドが返すのはダイジェストであり、値ではありません。平文を取り出すフラグも存在しません。これはGitHub Actionsのモデルに倣ったもので、シークレットは設計上、書き込み専用です。

ダイジェストが表示されます。値ではありません。 値を返すフラグはありません。平文を返すコマンドはどこにも存在しません。

これは GitHub Actions モデルです。書き込み専用です。--current <値> を渡して前提条件が通ることで、シークレットを知っていることを証明できます。Rediacc にシークレットの内容を教えてもらうことはできません。

ステップ5: 忘れたらローテーションする

値を忘れた場合は、覗こうとしないでください。ローテーションしてください。

rdc repo secret set --name my-app --key STRIPE_KEY --value sk_test_new --mode file --rotate-secret

シークレットの値を忘れた場合は、取り戻そうとするのではなくローテーションしてください。rotate-secret フラグは事前条件チェックをスキップし、監査ログにはその変更が意図的なローテーションとして記録されます。

--rotate-secret は前提条件チェックをスキップします。監査ログはこれをローテーションとして記録します。明示的で意図的な操作です。

以前の値を覚えていれば、代わりに --current <old-value> で証明できます。こちらがより安全な方法です。「間違ったターミナルか間違ったマシンで作業している」というミスに何度も引っかかっているので。

デプロイして配信を確認する

アプリに届かないシークレットはただのデータベースです。デプロイして両方の配信経路を確認します。

ステップ6: 両方のシークレットでデプロイする

rdc repo up --name my-app --machine <machine-name>

リポジトリをデプロイします。compose ファイルは両方のシークレットを利用します。env値は補間を通じて、ファイル値はDockerのシークレットマウントを通じて渡されます。

ステップ7: env シークレットが届いた

rdc term connect --machine <machine-name> --repository my-app --command 'docker exec app printenv DB_HOST'

コンテナ内で変数を出力します。postgres.internal。envモードのシークレットがデプロイ時にアプリに届いています。

コンテナが postgres.internal を表示します。アプリはデプロイ時に環境へ展開された値を実際に受け取っています。

ステップ8: file シークレットが届いた

rdc term connect --machine <machine-name> --repository my-app --command 'docker exec app cat /run/secrets/stripe_key'

コンテナ内で /run/secrets/stripe_key を読み取ります。ローテーション後の値がマウントされています。アプリは平文を取得できます。表示を拒否するのはCLIだけです。

コンテナ内の /run/secrets/stripe_key から読み込まれたローテーション後の値が表示されます。書き込み専用は人間と CLI に適用されます。アプリは Docker が約束する場所で本当の平文を受け取ります。

フォークの結末

フォーク後、シークレットリストは空

落とし穴を覚えていますか?リポジトリをフォークして確認してみましょう。

ステップ9: リポジトリをフォークする

rdc repo fork --parent my-app --tag test --machine <machine-name>

リポジトリをforkします。forkは親の暗号化イメージのバイト単位の完全なコピーです。

ステップ10: フォークは空をリストする

rdc repo secret list --name my-app:test

forkのシークレットを一覧表示すると空のセットが返されます。Stripeキーも、データベースパスワードも、APIトークンも存在しません。forkは親になりすますことができないため、本番環境のcloneが安全に行えます。

空です。

フォークには Stripe キーがありません。データベースパスワードもありません。API トークンもありません。フォーク内のコンテナは ${REDIACC_SECRET_STRIPE_KEY} を展開できません。/var/run/rediacc/secrets/<fork-id>/STRIPE_KEY というファイルは存在しません。

フォークはあなたのふりをすることができません。

ステップ11: フォークは起動すらできない

rdc repo up --name my-app:test --machine <machine-name>

親のcomposeでforkを起動しようとすると失敗します。forkのネットワークID以下にシークレットファイルが存在しないため、DockerはバインドマウントDockerを拒否します。本番の認証情報はforkに持ち込まれません。

デプロイは意図的に失敗します:bind source path does not exist: /var/run/rediacc/secrets/<fork-id>/STRIPE_KEY。シークレットファイルは親のネットワーク ID の下にあり、フォークのネットワーク ID の下にはないため、Docker がマウントを拒否します。この失敗こそがデモです。本番の認証情報はフォークに追いかけません。偶然にすら。

テスト用にフォークにシークレットが必要な場合は、サンドボックスの値で明示的に設定します。例:rdc repo secret set --name my-app:test --key STRIPE_KEY --value sk_sandbox_yyy --mode file。これでフォークは Stripe サンドボックスと通信し、クリーンに起動します。本番の認証情報は本番から出ません。

まとめ

  • rdc repo secret は認証情報をリポジトリイメージの外に置きます。
  • 両モードともコンテナに確実に届きます:env の展開と /run/secrets
  • get はダイジェストを返し、値は返しません。忘れたらローテーション。覗こうとしないでください。
  • フォークは空のリストを返し、親の compose すら起動できません。

シークレットはフォークが追えません。


次: バックアップとリストア