HN 日本語サマリー

← 一覧へ戻る
セキュリティ

鍵はどこへ?ラックレベルセキュリティのための鍵階層戦略

Has anybody seen my keys? A key-hierarchy strategy for rack-level security (rfd.shared.oxide.computer)

45 pointsby cyb0rg03 コメント

要約

本記事は、ラックレベルのセキュリティにおける鍵管理戦略について論じています。鍵階層アプローチを採用することで、セキュリティ体制を強化する方法を探求します。

全文翻訳

RFD 301 鍵はどこ?: ラックレベルセキュリティのためのキー階層戦略

背景と目的

Oxideラック内には、さまざまな種類の秘密情報が存在します。システムの基盤には、RoT(Root of Trust)に保存され、プラットフォームの識別や証明のための測定署名に使用されるDeviceIdおよびAliasキーがあります[[RFD 36]]。これらのキーと、一時的なDiffie-Hellman合意認証に使用される3番目のRoTホストキーペアは、スレッド(sled)がアプリケーションレイヤーメッセージのためのセキュアなスプロケット(sprocket)セッションを形成する能力を提供します[[RFD 238]]。これらのスプロケットセッションにより、各スレッドはメッセージの整合性が保護され、エンドポイントの認証と実行中のソフトウェアの証明が保証される、ポイントツーポイント方式で機密情報を共有できます。

ラックレベルのセキュリティ保証を提供し、攻撃者がスレッドやドライブのサブセットを持ち去っても有用な情報を回復できないようにするために、私たちはTrust Quorum(信頼カルテット)を設計しました[[RFD 238]]。

トラストカルテットの背後にある主要な保護メカニズムは、シャミアの秘密分散法(Shamir's Secret Sharing)です。これは、他のキーの導出元として使用される共有ラックレベル秘密情報であり、保管中のストレージを保護するために使用されます。ラックシークレットは、ディーラープロセスによってN個の一意なキーシェアに分割され、各ブートストラップエージェントに、N個のトラストカルテットメンバーの一意なプラットフォーム識別情報とともに、スプロケットセッションを介して配布されます。これらのプラットフォーム識別情報は、エンティティが主張通りであることを検証できるように、RoT上の公開キー証明書に焼き込まれます。この情報の配布後、ブートストラップエージェントは他のブートストラップエージェントへのスプロケット接続を確立し、グループメンバーシップ(交換された証明書経由)を検証し、K-1個のシェアを他のエージェントから取得して、K個のシェアからラックシークレットを再構築できます。重要なのは、K個のシェアを取得しない限り、ラックシークレットに関する情報は一切学習できないということです。

[[RFD 238]]で説明されているように、個々のシェアは各スレッドのM.2ドライブ上に暗号化されずに保存されます。攻撃者はラックシークレットを再構築するために少なくともK個のドライブを盗む必要がありますが、物理アクセス中にかなりの時間と混乱なしにこれを達成することは不可能です。将来的には、ルートオブトラスト(RoT)の助けを借りてこれらの秘密情報を「シール」し、スレッドの起動時にのみ復号化されるようにする予定です。シールにより、攻撃者はK個のスレッド全体を盗み、それらを起動してラックシークレットを回復する必要が生じます。K個のスレッドの重さは、カジュアルな攻撃者にとってはこれを非現実的なものにします。

このラックシークレットにより、グループ内の十分な数のN個のスレッドが同じラックに接続され、互いにシェアを配布するのに十分な信頼がある場合にのみ利用可能になる共有秘密情報が得られます。この共有秘密情報から、個々のU.2デバイスの暗号化キーを導出またはラップしたり、内部サービス用のラックレベルのルート証明書を個別に導出したりすることができます。

このRFDの目的は、以下の点を特定することです。

  • ラックシークレットによって保護されるデータは何か?

  • そのデータの時間的ライフサイクルは?

  • そのデータの空間的局所性は?

    • 特定のドライブ/スレッド/ラック/クラスターにローカルか?
    • 暗号化されたデータを移動させることを防ぐ物理的または論理的な制限は何か?
  • そのデータを保護するために使用されるキー階層は?

    • どのキーがどのデータを保護するか?
    • どのキーがどの秘密情報やキーから導出されるか?
    • どのキーが(他のキーによって)ラップ(暗号化)されるか?
    • キーはどのように導出またはラップされるか(キー スケジュール)?
    • キーの漏洩が発生した場合、どうなるか?

これらの質問は、私たちのラックのセキュリティにとって極めて重要であり、現在のソリューションでの経験や、姿勢を強化・拡大するために必要なソリューションを考え、実装するための時間に応じて、私たちの決定は進化する可能性があります。しかし、現時点では、このRFDは、保管中のデータを最大限に保護し、近い将来、他のセキュリティ目標に進むために、十分な回答を提供する必要があります。

リーフピーピング

私たちのラックレベルのキー階層は、ラックシークレットをルートとして開始します。トラストカルテットの各メンバーは、ラックシークレットを再計算するのに十分なシェアを学習できます。ラックシークレット自体はキーではないため、私たちのラックに必要なさまざまな目的で使用できるキー導出関数(KDF)を介して、そこからキーを導出する必要があります。どのキーを導出し、どのキーがそれらから導出され、どのキーをそれらがラップできるかは、システム内のどのデータが実際にキーによって保護されているかについての情報がなければ、決定するのは困難です。キー階層を構築し、ツリーの内部ノードを埋めるために、まずラック内で保護する必要があるデータのリストを作成し、そのデータを保護するために使用される一意のキー、およびそれらのキーから期待されるセキュリティ保証を列挙することから始めます。リーフキーを確立した後、キー階層の残りを埋めるために逆方向に作業できます。

以下の表は、現時点ではラックシークレットキー階層によって保護する必要があるデータを示しています。保護が必要な他のものも存在する可能性が高いですが、それらはほぼ同様の要件を持つでしょう。現在、Crucibleの暗号化キーをCockroachDBに保存していますが、MVP以降には、暗号化キー、証明書、認証トークンなどを格納する何らかのキー管理システムが導入されると予想しています。また、コントロールプレーンサービスが相互TLS(mutual TLS)で通信する方法を管理するための内部証明書発行局(CA)メカニズムもまだ定義されていません。しかし、そのようなシステムが必要になることはわかっているため、少なくともルート証明書とリーフ証明書が必要であることをここに明記しています。中間証明書が必要かどうかは未定です。

データストレージタイプストレージデバイスライフタイム
コントロールプレーンデータCockroachDB一部のU.2デバイスラックのライフタイムまで一時的
コントロールプレーンメトリクスClickhouse一部のU.2デバイス?
CrucibleエクステントファイルほとんどのU.2デバイスストレージボリュームのライフタイム
Crucibleボリューム暗号化キーCockroachDB一部のU.2デバイスストレージボリュームのライフタイム
ユーザー認証トークンCockroachDB一部のU.2デバイス一時的 / ユーザーセッションTTL
内部サービスリーフ証明書ファイル全てのU.2デバイス - ただし全てのデバイスに全ての証明書があるわけではない証明書TTL
内部CAルート証明書秘密鍵ファイル全てのスレッド?リーフ証明書のCSRに署名するのに十分な長さ、その後、リーフ証明書のローテーションのために再導出される可能性あり
内部CAルート証明書公開鍵ファイル全てのスレッドの1つ以上のU.2デバイス証明書ローテーションまで

保存されているデータはすべて、ファイルまたはデータベースとしてU.2デバイス上に存在し、最終的にはファイルになります。以前の議論から、ハードウェアベースのフルディスク暗号化(実装への信頼性の欠如、キー管理の複雑さ、複数の可能なベンダーのため)を使用しないこと、およびU.2デバイスごとに1つのzpoolを持つこともわかっています。ZFS暗号化を使用して、/cryptルートデータセットを介してほぼすべてのzpoolを暗号化したいと考えており、Crucibleのように独自の暗号化を提供する特定のデータセットは除外します。したがって、各U.2デバイス上のすべての必要な顧客データとコントロールプレーンデータが暗号化されていると仮定できます。キーの漏洩を制限するために、1つのディスクが盗まれ、そのキーが漏洩しても、他のすべてのディスクが漏洩しないように、ディスクごとに個別のキーを使用することを選択しました。各ディスクには大量のデータが含まれており、キーを変更する際にそのデータを再暗号化したくないことを知っています。幸いなことに、ZFSはキーの生成と変更方法において柔軟性を提供します。

キーのローテーションはZFSカーネルモジュールによって内部的に管理され、ユーザーのキーを変更してもデータセット全体を再暗号化する必要はありません。

— zfs(8) manページ

U.2上のすべての重要なデータをZFS暗号化(Crucibleキーを含む)を使用して暗号化しており、これらのZFSキーはそれらのディスクに保存されていないため、保管中のデータ保護要件を満たしています。ラックシークレットを再構築できない攻撃者は、ドライブまたはスレッドのサブセットを盗んで有用な情報を取得することはできません。次に、これらのキーを導出するかラップするか、および階層の残りの部分がどのようになるかを決定する必要があります。

ラックシークレットからのキー階層の構築

静的なクラスターを仮定すると、K個のキーシェアから計算でき、子キーを導出するために使用できるラックシークレットができました。以下の例では、図を小さく保つためにK = 2としています。

ラックシークレットは、ラックのロック解除後に使用されるシステム内のすべてのキーを保護します。階層内の次のキーを取得するには、2つのメカニズムがあります。

  • キー導出

  • キーラッピング(キー暗号化)

キー導出は、導出されたキーをディスクに保存する必要がないため、便利です。再生成するだけで済みます。問題は、導出されたキーが変更されると、下流の導出されたキーも変更されることです。

キーラッピングは、ラッパーキーがローテーションされた場合、下流のキーを変更する必要がないため便利です。これは、ストレージ上の大量のデータを暗号化する場合に特に役立ちます。親キーがローテーションされたという理由だけで、復号化してから再暗号化することを余儀なくされたくはありません。キーラッピングの欠点は、ラップされた(暗号化された)キーをディスク上のどこかに保存する必要があることです。同じキーが複数の場所で使用されている場合は、それを複製する必要があります。

トラストカルテットの場合、ノードが追加または削除されると新しいシェアが生成されるという特別な問題があります。新しいシェアで同じラックシークレットとキー導出を維持することは可能ですが、時間の経過とともにセキュリティが低下します。なぜなら、一度ラックシークレットを取得して保存した悪意のあるノードは、現在および将来生成されるすべてのドライブ上のすべてのデータにアクセスできるようになるからです。これは常に問題ですが、既知の漏洩の場合にラックシークレットのローテーションを許可することを優先します。したがって、トラストカルテットの再構成(設定変更)やその他のキーシェアのローテーションのたびに、常に新しいラックシークレットを生成します。これにより、ラック上の既存のすべてのデータが漏洩した場合でも、少なくとも漏洩したスレッドが削除されれば、新しいデータは漏洩しません。

現在、セキュリティ目標について何を知っていますか?私たちは以下を行いたいと考えています。

  • ラックシークレットからできるだけ多くのキーを導出し、ラップされたキーを保存する必要性を制限する

  • ラックシークレットのローテーションを許可し、漏洩したラックシークレットを軽減できるようにする

  • U.2ドライブごとに一意の暗号化キーを使用し、1つのキーが漏洩しても他のキーは漏洩しないようにする

  • 新しい(空の)スレッドが、再構成が発生した場合に共有されていない古いスレッドの保管データにアクセスできないことを保証する

そして、これらの目標から生じる制約は何ですか?私たちは以下を行う必要があります。

  1. ラックシークレットがローテーションされるときに、U.2ドライブごとのZFSラッパーキーを変更できること。これには、古いキーと新しいキーの両方に同時にアクセスできる必要があります。

  2. 新しい再構成がコミットされたことをすべてのスレッドが同時に知るわけではないという事実を考慮に入れること、およびキーを変更するタイミングを、キーローテーションの分散性を考慮して考慮に入れること。

  3. 新しいラックシークレットのコミットメント、したがって新しいラックシークレットが複数の「誤った開始」の後に発生する可能性があることを認識すること。新しい再構成が複数のスレッドに配布されるがコミットされない場合。

最初の制約は、ディスク暗号化の一般的な使用法から与えられます。後者の2つの制約は、再構成問題の分散性から生じ、[[RFD 238]]で詳しく説明されています。2番目の制約により、コミット時に新しいキーシェアを配布することが不可能になります。なぜなら、スレッドが新しいエポックにコミットした後、新しいラックシークレットのみを使用するはずであり、コミットしたことをまだ学習していないスレッドから新しいキーシェアを要求する必要があるかもしれないからです。コミット時にキーシェアを配布しないことには、[[RFD 238]]でさらに詳しく説明されている他のセキュリティ上の理由もあります。したがって、2フェーズコミットプロトコル([[RFD 238]])を考慮すると、準備メッセージで新しいシェアを配布する必要があります。3番目の制約により、新しいラックシークレットから導出されるキーがコミットされない場合、ZFS暗号化ラッパーキーを学習した直後に変更することが不可能になります。コミットされたラックシークレットがないと、ラックシークレットを再計算してZFS暗号化ラッパーキーを再導出するために必要なシェアを取得することは不可能になります。

これらの目標と制約を考慮すると、古いグループと新しいグループの両方のメンバーであるスレッドは、古いラックシークレットと新しいラックシークレットの両方に同時にアクセスできる必要があることを再確認します。これは、U.2デバイスのZFS暗号化ラッパーキーを導出して変更できるようにするために必要です。再構成プロトコルの単純化と、古いラックシークレットの露出を制限するために、スレッドが現在コミットされている構成のキーシェアのみを配布できるようにしたいとも考えています。上記の要件と要望は対立しているため、この状況をどのように処理するかについて創造的になる必要があります。

この難局を解決する最も簡単な方法は、再構成中のディーラーを介することです。 [[RFD 238]]で説明されているように、各構成に単調増加するエポック番号を付けます。エポック1では、各スレッドは単一のシェアを受け取り、ラックシークレットを再計算して元のU.2デバイスのZFS暗号化ラッパーキーを導出します。再構成が発生すると、ディーラーは十分なシェアを取得して現在のコミットされたエポック(例では1)のラックシークレットを再計算します。ディーラーはエポック2の新しいラックシークレットを生成し、それをキーシェアに分割します。ディーラーはまた、エポック2のラックシークレットからエポック1に関連付けられたold-rack-secret暗号化キーを導出し、エポック1のラックシークレットをold-rack-secretキーで暗号化します。そして、トラストカルテットの準備メッセージの残りの部分とともに、この暗号化された秘密情報を新しいグループのメンバーに送信します。エポック2の新しい構成がコミットされるまで、エポック1の暗号化されたラックシークレットは復号化できません。なぜなら、エポック1のラックシークレットを復号化するために必要なキーを導出するために必要なエポック2のシェアを、どのメンバーも送信しないからです。エポック2がコミットされない場合、この状態は続きます。

スレッドが新しいエポックの構成がコミットされたことを認識するとすぐに、新しいラックシークレットのシェアを取得し、それを再計算し、準備メッセージ内の古いラックシークレットを復号化し、ストレージキーを導出し、ストレージ暗号化を再構成し、その後、暗号化されたラックシークレットとメモリ内の古いラックシークレットを安全に削除します。失敗モードに関するいくつかのニュアンスがありますが、それはこのRFDには関係なく、[[RFD 238]]でさらに詳しく説明されています。

これで、キー階層がほぼ文章で説明され、再構成中のスレッドの障害を許容し、U.2ドライブのZFS暗号化に使用される暗号化ラッパーキーの変更を可能にしました。また、ラックシークレットの漏洩を処理し、個々の導出キーの漏洩の露出を制限します。これを念頭に置いて、キー階層の図を描くことができます。

キー導出

注記

ここでは、CA関連の証明書やその他のキーは無視し、ストレージ暗号化キーとラックシークレット暗号化キーに焦点を当てています。これらはMVPにとって最も関連性が高いです。証明書やその他のキーの使用方法が他のRFDで定義されるにつれて、同様の技術が適用されます。

スプロケット([[rfd256]])と同様に、キー導出関数(KDF)としてsha3-256をハッシュアルゴリズムとして使用します[[hkdf]]。また、ラックシークレットの再構成中の暗号化にはchacha20poly1305を使用します。ZFS暗号化には暗号化オプションが限られているため、最も強力なAES-GCM-256を選択します。特に、AES-GCM-256とchacha20poly1305はどちらも32バイト(256ビット)のキーを使用するため、常にHKDFから32バイトのキーを導出します。

HKDFは2段階のアルゴリズムであり、最初のextractステップでsaltを必要としませんが、強く推奨します。saltはキーマテリアルから独立しており、少なくとも使用されるハッシュ関数と同じ長さである必要があり、秘密にしておく必要はありません。理想的には、saltは一方の当事者によって生成されるべきではありませんが、私たちはすでにディーラーにラックシークレットの生成を依存しています。実装の容易さのため、ディーラーは各エポックごとにランダムに32バイトのsaltを選択し、それをキーシェアおよびメンバーシップデータとともにブートストラップエージェントに含めます。このsaltは、古い暗号化されたラックシークレットのキー導出にのみ使用され、ディスク暗号化キーには使用されないことに注意してください。これはある種の歴史的遺物であり、どちらの場合もセキュリティ上の保証をあまり提供しているとは言えません。その理由は、以前のラックシークレットに関連するキーマテリアルを生成するためのHKDF-expandは、トラストカルテットコード自体によって実行されるのに対し、ディスクキー生成は別のモジュールkey-managerで行われ、そこには入力キーマテリアル(ラックシークレット)のみが渡されるためです。ディーラーから別のsaltが配布されることはなく、saltを共有するか、新しいsaltを追加することになります。いずれの場合も、キーマネージャーにそれを渡す必要があり、セキュリティの向上は明確ではなく、追加の作業となります。

HKDF-Extractを使用して、ikm(および必要に応じてsalt)を一様に分布した出力キーマテリアルに抽出した後、HKDF-Expand部分のHKDFアルゴリズムを使用して、この出力から個々のキーを導出できます。出力導出キーをその使用法とコンテキストにバインドし、同じキーの他の導出者によって他の目的で使用されるのを防ぐために、HKDF-Expand関数にinfoパラメータが提供されます。

私たちのシナリオでは、生成しているキーはストレージ暗号化キーとラックシークレット暗号化キーの2種類です。これらをコンテキスト化して、一方のキーがもう一方として誤って使用されないようにしたいと考えています。また、個々のドライブと構成エポックごとにさらにコンテキスト化して、同じ目的のキーが混同されないようにしたいと考えていますが、異なる特定のユースケースのキーは混同されないようにします。HKDFに渡すinfo文字列を以下に定義します。ここで+は連結演算子です。

  • ラックシークレット情報: `