
2026/09/10 6:59
10 倍の RBAC は存在しない
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
Infisical は、機密管理プラットフォームにおいて重大なアクセス制御の脆弱性を解消するため、不安定だった既存システムを堅牢なフォルダーベースのアーキテクチャに置き換えました。核心的な向上点は、文字列ベースのパス保存から安定したユニーク識別子(UUID)への変更と、権限をリスト、読み取り、編集、管理、完全アクセス、およびフォルダーへのアクセス付与の 5 つの直感的なティアに標準化することです。このアップグレードは、特にミックスされた資格情報に関連する複雑なケース(例:特定のアプリ役割と共にエンジニアリングへのアクセスを付与する場合)や、安全な権限削減を取り扱い、従来の「追加特権」モデルではバグ引き起こすリネームや複雑なロジックにより処理が困難でした。
安定性を確保するため、エンジニアリングチームはオープンソース代替案(SpiceDB や OpenFGA など)への移行ではなく、内部モデルの改良を選択しました。これにより、関連する破壊的な変更を回避しています。新しいシステムは、直接メンバーシップ、グループ、そして組み込み役割を「拒否してから許可する」ロジックに統合しており、これは特定のフォルダー ID を中心としたもので、フォルダーへの付与が継承されたアクセス権を上書きします。さらに、プロジェクト単位でのバージョンカウンターを実装し、フォルダーの再構成(リネーム、移動、削除)中にキャッシュを即座に無効化してデータ整合性を瞬時に保っています。これらの変更は、偶発的な資格情報の露出や展開失敗のリスクを大幅に低減し、構造変化のあらゆる状況下でも請求処理のような機密な操作が信頼できるように保証すると同時に、ユーザーの安全性と企業の評判を保護します。
本文
複雑なエンジニアリング課題を解決したシンプルな機能のリリース:Infisical のフォルダベース RBAC
最近、多くの顧客が気づいていないかもしれない複雑なエンジニアリング課題を解決するシンプルな機能をリリースしました。Infisical のユーザーは、今や単一フォルダへのアクセス権付与および拒否を行うことができます。このような機能は、「全社会会議(all-hands)」での短い言及や 1 行だけの変更ログエントリーとして扱われる典型的なものです。
ロールベースのアクセス制御(RBAC)、許可、認証は、請求処理スキーママイグレーション監査ログと同じカテゴリーのエンジニアリングプロジェクトです:
- 期待される標準的な機能である
- ユーザーは可能な限り時間をかけずに利用する
- シンプルに見えても難易度の高いエンジニアリング課題である
理想とする最良の結果とは、不満の声がなくシステムが正しく動作することですが、それだけでは卓越することはできません。「RBAC システムを Twitter で絶賛している」といった 10 倍の革新性はありません。アクセス制御は製品がチェックする項目の一つではありますが、いわゆる「キラー機能」ではありません。
それでも、それらが重要な役割を果たすことを否定するものではありません:
- エンタープライズ系のお取引先様にとって、これらの機能が備わっていなければ注文書への署名はあり得ません。
- シークレット管理において、アクセス制御を誤ると、権限を持たない者が機密情報を閲覧してしまうか、サービスがシークレットへのアクセスを失い早朝 3 時に顧客のデプロイが失敗するといった事態を引き起こす可能性があります。
Tailscale や Oso も、認証・認可(Authorization)は見た目ほど難しいことを執筆しており、フォルダベースのアクセス制御を実装した経験も別の視点から同じ教訓をもたらしました。
フォルダベースのアクセス制御を実装した理由
概念的には RBAC はシンプルです。各アイデンティティ(人間か機械であってもよい)は、役割(例:管理者、メンバー、ゲストなど)を持ち、その役割には権限が付与されています:
- 行動(Actions):何ができるか
- サブジェクト(Subjects):行動の対象となるものは何か
私たちは CASL を使用しており、役割に付与される基本的な権限は概ね以下のようになります。
{ "action": ["readValue", "create"], "subject": "secrets", "conditions": { "environment": "staging" } }
このアイデンティティは、ステージング環境でのみシークレットの閲覧・作成が可能です。CASL では明示的に許可されていないものは自動的に拒否されます。上記のような権限を持つ役割であれば、プロダクション環境のシークレットには決して触れることはできません。
ロールベースのアクセス制御が普及しているのは、人間の直感に合致するからです。RBAC における役割はチーム内の役職と対応しており、各チームメンバーごとに個別に設定を行うよりも一括で権限の付与・拒否を処理できるため、時間を節約できます。これによりアクセス制御を専任の仕事にする必要がなくなります。
しかし、すべてのアクセス制御の変更が完全に役割に基づいているわけではありません。フォルダベースのアクセス制御を実装した理由は、以下の2 つの特異ケースにあります:
-
特異ケース 1:NetSuite 統合の制約
- そのチームで NetSuite の経験を持つエンジニアが唯一人いる場合、NetSuite 統合の開発という義務に追いやられてしまいます。
- このエンジニアには、エンジニアリング役割の認証情報に加え、NetSuite 認証情報が別途必要です。
-
特異ケース 2:特定権限からの剥奪
- 他のケースでは、特定の権限から役割を剥奪する必要があります。
- 例:AWS Lambda 関数のリファクタリングに招かれたスペシャリストコントラクターはエンジニアリング系の認証情報は必要ですが、財務データを閲覧する必要はありません。
- つまり、エンジニアリング役割の認証情報から請求関連の認証情報を除いた状態が必要です。
いずれの場合も、人物の役割自体は変化しませんでしたが、アクセス要件は変化します。従来の RBAC でこれを解決しようとすると、以下のような 2 つの不満足な選択肢しか残りません:
- エンジニア役割を編集すると、不要なアクセス権が付与されるか、必要なアクセス権が失われます。
- カスタム役割を作成すると、一過性の用途に対して並列的な設定が必要となり、ドリフト(配置の不一致)の原因となります。
Infisical では、「追加の権限(Additional Privileges)」という第 3 の選択肢を持っていました。これはフォルダベースの RBAC を現在置換しているものです。「追加の権限」は役割の上に追加される個人ごとの付与でした。機能はしましたが、やや複雑で(授与可能なすべての権限を表示するため)、また権限を剥奪することができませんでした。
この仕組みは強力でしたが、直感的ではありませんでした。Infisical のすべての概念、データ階層構造、および特定のインスタンスの設定を理解する必要がありました。すべてのユーザーがこの知識を持っていると仮定できませんため、「追加の権限」は以下のような 2 つの失敗モードに陥るリスクがありました:
- ユーザーが望む権限を誤って構成し、余分なアクセスや不十分なアクセスを提供してしまう。
- 「そのための時間はない」と判断して広範なアクセスを付与し、セキュリティ態勢を損なう。
また、「追加の権限」はパスを文字列で保存しており、フォルダ名を変更すると無効化されていました。一方、フォルダには
folderID という UUID が付与されており、フォルダの名前を変更したり環境間を移動したりしても、フォルダベースのアクセス制御の付与は無効化されません。
ユーザーにとって直感的な解決策が必要でした。そのため、フォルダが適した場所でありました。Infisical ではデフォルトのフォルダ構造を提供しないためです。フォルダが存在する以上、なぜ存在し、中身は何なのかについては、その置かれたユーザー自身によって理解されているはずです。フォルダベースの RBAC は、ユーザーの頭でアクセス問題をどのように捉えているかを、Infisical 内の設定として翻訳するプロセスを簡素化します。
ユーザー側への出力はシンプルになりつつも、エンジニアリング作業としては複雑でした。既存のモデルを単に拡張しただけでは対応できなかったためです。
なぜ当社の RBAC システムを拡張することがこれほど複雑だったのか
フォルダ用のアクセス付与方法だけを新たに追加することはできませんでした。すでにシークレットへのアクセス手段は多種多様にあるためです。Infisical でアクセスを定義する方法のいくつかだけを紹介しましょう:
- ユーザーやマシンアイデンティティがプロジェクトに直接メンバーとして所属している。
- グループがメンバーシップを保持し、人間とマシンの両方のアイデンティティを含む場合がある(SCIM、SAML、OIDC、LDAP などを通じてよく到着します)。
- 役割は組み込みタイプのカスタムタイプに分けられます。
- 非推奨のサービストークンは独自のスコープモデルを持ち、現在も評価しています。
これは混乱しているように見えますが、多くの顧客からの「はい」への答えを重ねるにつれて複雑化するのは RBAC システムの特徴です。Infisical はオープンソースであるため、これらを非推奨にすると、どこかのセルフホスト環境で問題を引き起こす可能性があります。また、シークレットは極めて重要なミッションであり、ミスのせいでサービス停止やエンジニアリング組織全体が停滞する可能性があります。
さらに、SpiceDB や OpenFGA のような Zanzibar スタイルのシステムへの移行を行っていないことも理由です。移行する場合、すべての権限チェックを再実装し、セルフホストの顧客にステートフルなサービスを追加して実行させるだけでなく、破壊的な変更を導入することになります。
構築するアクセス制御システムは、既存のすべてと互いに動作する必要がありました。したがって、このプロジェクトは以下の 2 つのフェーズに分けて取り組む必要がありました:
- まず、権限を標準化する。
- 次に、正しい権限を適用するためのロジックを実装する(後者はより難しいエンジニアリング課題ですが、前者はその成功のための基礎でした)。
私たちが構築した 5 つの標準的な権限階層
最初に、5 つの権限階層のリストを作成しました。これらは汎用性の高い「読み書き」とは異なりますが、非常に直感的です。
| 階層 (Tier) | 追加される機能 |
|---|---|
| List(一覧表示) | シークレットが存在し、その名前を確認可能(値自体は見えない)。 |
| Read(閲覧) | 値を読み取り、コミット履歴を閲覧し、動的シークレットのリースを取得可能。 |
| Edit(編集) | シークレットとインポートの作成、編集、削除が可能。 |
| Manage(管理) | ローテーション構成、動的シークレット、同期、ハニートークンの設定が可能。 |
| Full Access(完全アクセス) | 他のユーザーにフォルダへのアクセス権を付与可能。 |
これらの権限は、フォルダベースのアクセス制御の実装における構成要素です。技術的には、フォルダベースの RBAC は「追加の権限」として保存されます。
additional_privileges テーブル内の行として存在し、ユーザーのアイデンティティや付与が一時的かどうかといった一部の列を再利用します。
この機能では以下の 2 つの新しい列が導入されました:
:上記の表から選択する権限階層。role
:アクセスを付与対象とするフォルダ。folderID
従来の「追加の権限」には、これらの新しい列のいずれかについては
null が設定されており、これにより新しいフォルダベースの付与と区別できます。この仕組みにより、「追加の権限」が既存のユーザーで引き続き機能する一方で、特定のチェックによって両方のモデルを併用しようとする行は検出されます。
つまり、フォルダベースの優先権限を設定することができました。次に、その権限を適用するためのロジックを実装する必要がありました。
フォルダベースのアクセス制御を「優位」にすること
フォルダ上で意図的にアクセスを設定することは、役割やグループ、その他の権限から継承されるすべての設定を上書きします。このことを確保することは容易ではなかった。その理由は、我々が構築しようとしている目的が、アイデンティティの役割が付与するアクセスと、そのアイデンティティが持つべきアクセスとの衝突を解消することにあるからです。各オブジェクトへのアクセスは複数の場所で構成されている可能性があるため、フォルダベースのアクセス付与が常に優位であるように確保する必要がありました。
CASL のチェックでは、一致する最後のルールが優先され、「拒否(deny)」は「許可(allow)」に勝ります。既存のシステムすべてを機能させ続けるために、フォルダベースのアクセス制御を 2 つの層に分けました:
- 第 1 層:既存のルールを保持する。
- 第 2 層:その後に取り付けられるブロックです。これは、そのフォルダに対するすべての動作を拒否するルールブロック(deny rules)で始まり、次に付与されている階層(tier)に応じて許可されるルールが続きます。
[ // コントラクターの「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" } } } ]
拒否ブロック(deny block)は常に同一であり、各階層は許可される内容を決定するだけです。拒否の役割を持つのはどの階層もありません。この拒否リストは、パススコープの権限を新しいものとして追加した場合でも拡張しない限りテストで失敗するように実装されています。
カスタム役割と権限、また従来の「追加の権限」も引き続きサポートされますが、このロジックにより、より直感的なフォルダベースの RBAC が他の付与を上書きするように動作します。
最後に、キャッシュに正しい権限が格納されていることを確保する必要がありました。
キャッシュ無効化の問題をどのように解決したか
頻繁に変更される可能性がある権限に関する情報のキャッシングは困難です(キャッシュ無効化そのものが常に難しいため)で、すべての更新が応答速度を低下させることなく即時に反映される必要があるからです。
Infisical では権限チェックがほぼすべてのリクエストで行われるため、結果をキャッシュしフィンガープリントを使用して検証しています。しかし、フォルダベースの付与はこの仕組みを破り、修正には複数の試行が必要でした:
- 第 1 回試み:他のものと同様に付与(grant)行に対してフィンガープリントを適用する。
- 付与はフォルダ ID を格納しますが、ルールにはパスが必要であるため、コンパイル時に ID を解決します。
- これにより、フォルダ名を変更するとそのフォルダに関連するすべての付与のコンパイル結果が異なってしまいます。
- 第 2 回試み:付与が指し示すフォルダに対してフィンガープリントを適用する。
- これは失敗しました。パスは祖先チェーンを上ることで構築されるため、
の名前を変更すると、/a
への付与の解決先が変わりながら、/a/b
の自らの行自体は変化しないからです。したがって、ツリーの大部分に対してフィンガープリントを適用しなければなりません。/a/b
- これは失敗しました。パスは祖先チェーンを上ることで構築されるため、
最終的に、プロジェクト単位でバージョンカウンターを実装しました。これは各行への書き込み操作(フォルダの名前変更、移動、削除を含む)ごとに増分されます。キャッシュキーではなく、フィンガープリント内に読み込まれます。
結論
RBAC を構築することは華々しくはありませんが、極めて重要です。人々が製品の RBAC や請求処理システムなどの素晴らしさを称賛することはないかもしれませんが、逆にそこを誤れば苦情が保証されるという事実があります。
だからこそ、単に**「うまく動くようにする」ことが重要なのです**。