HN 日本語サマリー

← 一覧へ戻る
プログラミング

10倍すごいRBACは存在しない

There is no 10x RBAC (infisical.com)

56 pointsby FinnLobsien13 コメント

要約

Infisicalは、従来のRBAC(ロールベースアクセス制御)が抱える複雑さや、個別のユースケースへの対応の難しさを解消するため、フォルダベースのRBACを導入しました。RBACは必要不可欠ながらも目立たない機能であり、Infisicalの新機能は、ユーザーが直感的に理解しやすいフォルダ単位でのアクセス管理を提供することで、この課題に対処しています。

全文翻訳

Zanzibarへの移行なしに複雑な権限を再構築した方法

最近、ほとんどの顧客が決して気づかないかもしれない、しかし複雑なエンジニアリングの成果であるシンプルな機能を構築しました。Infisicalのユーザーは、単一のフォルダへのアクセスを許可/拒否できるようになりました。これは、オールハンズミーティングで軽く触れられ、変更履歴に一行だけ記載されるような種類のものです。

RBAC、権限、認可は、請求、スキーマ移行、監査ログと同じクラスのエンジニアリングプロジェクトです。

  • それらは期待されるユーティリティである
  • ユーザーはそれらに費やす時間を最小限にする
  • それらは、単純に見えても、トリッキーなエンジニアリングプロジェクトである

最良のケースは、苦情がなく、システムが正しいことです。決して卓越することはありません。Twitterで人々が絶賛するような10倍優れたRBACシステムは存在しません。アクセス制御は、製品がチェックを入れるボックスですが、キラー機能ではありません。

だからといって、それらが重要でなくなるわけではありません。エンタープライズバイヤーは、それなしでは注文書に署名することはありません。そして、シークレット管理(Infisicalが事業を展開するカテゴリの1つ)では、アクセスを間違えると、誰かが本来見るべきではない認証情報を見てしまうか、サービスがシークレットへのアクセスを失ったために顧客のデプロイメントが深夜3時に失敗する可能性があります。

Tailscale と Oso は両方とも、認可が予想以上に難しい理由について書いています。フォルダベースのアクセスを構築したことで、私も異なる方向から同じことを学びました。

なぜフォルダアクセスを構築したのか

概念的には、RBACはシンプルです。人間または機械の各IDには、権限を持つロール(例: admin、member、guest)があります。

  • アクション: 何ができるか
  • 対象: 何に対してアクションを実行できるか

私たちはCASLを使用しており、ロールにアタッチされた基本的な権限は次のようになります。

{
  "action": ["readValue", "create"],
  "subject": "secrets",
  "conditions": { "environment": "staging" }
}

このIDは、ステージング環境のシークレットを読み取り、作成できます。CASLは、明示的に許可されていないものはすべて自動的に拒否します。上記の権限を持つロールは、本番環境のシークレットに触れることは決してできません。

ロールベースアクセスが人気があるのは、人間が直感的に理解できるからです。RBACのロールはチーム内のロールに対応しており、各チームメンバーの権限を個別に設定するよりも、一括で権限を付与/拒否することで時間を節約できます。これは、アクセス制御をフルタイムの仕事に変えてしまうでしょう。

しかし、すべてのアクセス制御の変更がロールと完全に一致するわけではありません。フォルダベースのアクセスを構築した理由には、2つの特別なケースがあります。

チームで唯一Netsuiteの経験を持つエンジニアを例に挙げます。彼はNetsuiteインテグレーションを構築するという特権を課せられ得ました。

そのエンジニアは、engineerロールの認証情報に加えて、Netsuiteの認証情報が必要です。

他のケースでは、特定のロールから権限を剥奪する必要があります。AWS Lambda関数をリファクタリングするために連れてこられた専門の契約者は、エンジニアリングの認証情報が必要ですが、財務データを見せる理由はありません。

その契約者は、engineerロールの認証情報が必要ですが、請求に関する認証情報は不要です。

これらのどちらのケースでも、その人のロールは変わりませんが、アクセス要件は変わります。従来のRBACでこれを解決しようとすると、2つの不満な選択肢しかありません。

  • engineerロールを編集すると、不要なアクセスが付与されるか、必要なアクセスが削除される。
  • engineerロールに基づいた変更されたカスタムロールを作成すると、一度限りのユースケースのために並列構成が作成され、ドリフト(設定のずれ)につながる可能性がある。

Infisicalでは、フォルダベースRBACが現在置き換えている「追加権限」という3番目の選択肢がありました。これはロールの上に成り立つ、個人ごとの付与でした。それは機能しましたが、あまりにも複雑すぎました(考えられるすべての権限を付与できるインターフェースでした)し、権限を減らすことができませんでした。

[画像: Infisicalのレガシーな追加権限インターフェースを介して権限を付与する様子]

これは強力でしたが、直感的ではありませんでした。Infisicalのすべての概念、データ階層、および特定のインスタンスの設定を理解する必要がありました。すべてのユーザーにこれを期待することはできません。そのため、追加権限は2つの障害モードのリスクがありました。

  • ユーザーは、プロビジョニングしたい権限を誤って設定し、過剰または過小なアクセスを提供する可能性がある。
  • ユーザーは「そんな時間はない」と言って、より広範なアクセスを付与する可能性があり、それはセキュリティ体制を損なうことになる。

追加権限はパスを文字列として保存していたため、フォルダの名前を変更すると壊れていました。フォルダにはfolderID UUIDがあるため、フォルダの名前を変更したり、別の環境に移動したりしても、フォルダベースのアクセス付与は壊れません。

ユーザーにとって直感的なソリューションが必要でした。フォルダは適切な場所でした。なぜなら、デフォルトのフォルダ構造を提供していないからです。フォルダが存在する場合、ユーザーはその理由と中身を知っています。なぜなら、ユーザー自身がそれを作成したからです。フォルダベースRBACは、ユーザーの頭の中にあるアクセス問題の翻訳をInfisicalの設定に単純化します。

ユーザー側の結果はシンプルですが、エンジニアリング作業は複雑でした。なぜなら、既存のモデルを単に拡張するだけでは不十分だったからです。

なぜRBACシステムが拡張するには複雑すぎたのか

フォルダにのみ有効なアクセス付与方法をもう1つ追加することはできませんでした。なぜなら、シークレットにアクセスできる方法はすでに数多く存在するためです。Infisicalでアクセスを定義するいくつかの方法を以下に示します。

  • ユーザーはプロジェクトに直接メンバーになることができ、マシンIDも同様です。
  • グループがメンバーシップを保持し、人間とマシンの両方のIDを含めることができます。これらはSCIM、SAML、OIDC、またはLDAPから到着することがよくあります。
  • ロールは組み込みまたはカスタムにできます。
  • 非推奨のサービストークンには、まだ評価中の別のスコープモデルがあります。

これは乱雑に見えるかもしれませんが、ほとんどのRBACシステムは、顧客からの多くの要求に対して「はい」と答えるうちに複雑さを蓄積します。Infisicalはオープンソースであるため、自己ホスト型インスタンスで問題を引き起こす可能性なしに、これらのいずれかを非推奨にすることはできません。シークレットはミッションクリティカルでもあるため、間違いはダウンタイムを引き起こしたり、エンジニアリング組織を停止させたりする可能性があります。

また、SpiceDBやOpenFGAのようなZanzibarスタイルのシステムに移行しなかったためでもあります。それらを導入することは、すべての権限チェックを書き直し、すべての自己ホスト型顧客に別のステートフルサービスを実行させ、破壊的な変更を導入することを意味します。

構築するあらゆるアクセスシステムは、すでに存在するすべてのものと連携する必要がありました。これは、プロジェクトを2つのフェーズで取り組む必要があったことを意味します。

まず、権限を標準化する必要がありました。次に、正しい権限を適用するロジックが必要でした。後者の方がエンジニアリング的には難しい問題でしたが、前者がその基盤となりました。

[画像: InfisicalでIDにフォルダスコープのアクセスを付与する様子]

構築した5つの標準的な権限ティア

最初に5つのティアの権限リストを作成しました。これらは一般的な読み取りおよび書き込み権限よりもカスタムですが、非常に直感的です。

ティア追加されるもの
List値なしで、シークレットが存在すること、およびその名前を確認できる
Read値の読み取り、コミット履歴の表示、動的シークレットのリース取得
Editシークレットとインポートの作成、編集、削除
Manageローテーション、動的シークレット、同期、ハニートークンの設定
Full Access他のユーザーにフォルダへのアクセスを付与する

これらの権限は、フォルダベースアクセスの実装のビルディングブロックです。技術的には、フォルダベースRBACは追加権限として保存されます。これらはadditional_privilegesテーブルの行として存在し、ユーザーのIDや付与が一時的であるかどうかなどの一部の列を再利用します。

この機能は2つの新しい列を導入します。

  • roleは上記のテーブルの権限ティアです。
  • folderIDは付与がアクセスを提供するフォルダです。

レガシーな追加権限には、これらの新しい列のいずれかにnullが含まれており、新しいフォルダベースの付与と区別されます。これにより、追加権限は既存のユーザーに対して引き続き機能しますが、一部のチェックにより、いずれかのモデルを両方使用しようとする行がないことが保証されます。

これにより、フォルダベースの権限が設定されました。次に、それらの権限を適用するために必要なロジックが続きました。

フォルダベースアクセスを「勝たせる」方法

フォルダへのアクセスを意図的に設定することは、ロール、グループ、その他の権限から継承されたすべてを上書きします。これは簡単ではありませんでした。なぜなら、それを構築している主な理由は、IDのロールが付与するアクセスと、それが持つべきアクセスとの衝突だったからです。IDは、複数の場所でオブジェクトへのアクセスが構成されている可能性があるため、フォルダベースのアクセス付与が常に優先されるようにする必要がありました。

CASLチェックでは、最後に一致したルールが優先され、denyはallowに勝ちます。そのため、既存のすべてのシステムを機能させ続けるために、フォルダアクセスを2つのレイヤーに分けました。

  • レイヤー1は既存のルールを保持します。
  • レイヤー2はその後に追加されます。これは、そのフォルダ上のすべての操作をカバーする拒否ルールのブロックであり、次に付与が持つティアの許可ルールが続きます。
[
  // 契約者の「engineer」ロールから、変更なし
  {
    "action": ["readValue", "create", "delete"],
    "subject": "secrets",
    "conditions": { "environment": "prod" }
  },

  // レイヤー2: /payments内のすべてを拒否
  {
    "action": ["readValue", "create", "delete"],
    "subject": "secrets",
    "inverted": true,
    "conditions": { "environment": "prod", "secretPath": { "$eq": "/payments" } }
  },

  // その後、Readティアが許可するものだけを許可バックする
  {
    "action": ["readValue"],
    "subject": "secrets",
    "conditions": { "environment": "prod", "secretPath": { "$eq": "/payments" } }
  }
]

拒否ブロックは常に同一であり、ティアは許可されるものを決定するだけです。どのティアも拒否を担当しません。その拒否リストは、パススコープの権限を拡張せずに新しいパスを追加した人がいないかを確認するテストによって検証されます。

カスタムロールや権限、およびレガシーな追加権限は引き続きサポートされていますが、このロジックにより、より直感的なフォルダベースRBACが他の付与よりも優先されるようになりました。

最後に、キャッシュが正しい権限を格納していることを確認する必要がありました。

キャッシュ無効化を解決した方法

変更の対象となる権限のキャッシュは困難です(キャッシュ無効化は常にそうである)が、応答時間を遅くすることなく、すべての更新が即座に有効になることを保証する必要がありました。

Infisicalでは、権限チェックは基本的にすべてのリクエストで行われるため、結果をキャッシュし、フィンガープリントで検証します。フォルダの付与はそれを壊し、修正するには複数の試行が必要でした。

  • 最初の試み: 付与行をフィンガープリントする。他のすべてと同様に。付与はフォルダIDを格納しますが、ルールにはパスが必要です。そのため、コンパイル時にIDを解決します。これは、フォルダの名前を変更すると、その上のすべての付与が異なるようにコンパイルされることを意味しました。
  • 2番目の試み: 付与が指すフォルダをフィンガープリントする。これは失敗しました。なぜなら、パスは祖先チェーンをたどって構築されるためです。/aの名前を変更すると、/a/b上の付与が解決されるパスが変わりますが、/a/b自体の行は変更されません。ツリーのほとんどをフィンガープリントする必要があるでしょう。

最終的に、プロジェクトごとにバージョンカウンターをリリースしました。これは、フォルダの名前変更、移動、削除を含む、指定された行への各書き込み操作でインクリメントされます。これは、キャッシュキーではなく、フィンガープリントに読み込まれます。

結論

RBACの構築は華やかではありませんが、重要です。そして、人々が製品のRBAC、請求、または同様のシステムについてどれほど素晴らしいかをコメントすることはまずありませんが、その逆も真なりです。ここで何かを間違えると、必ず苦情が発生します。

だからこそ、「うまく機能する」ようにすることが重要です。