OCIのポリシーステートメントは削減できるのか

6/17のOCIjpの話で、IAMポリシー・ステートメントの上限の話があったので、ちょっといろいろ考えてみました。

# 書きたいこと

IAMポリシー・ステートメントの上限は気にしたことは今までなかったですが、上限があることを見越したIAM設計が今後必要だなと思い、どういう手が使えるのかを考えてみた結果を書いてみたいと思います。

[リリースノートが見つけられなかったので、Updateセミナーの資料にリンク](https://speakerdeck.com/oracle4engineer/oracle-cloud-update-summary-202504?slide=25)。

なんか、Ciscoスイッチの、TCAM容量と格闘していた頃を思い出しますね。リソースは有限なのです。


# まずはリソース制限の確認

見る場所はもちろん[IAMのサービス制限](https://docs.oracle.com/ja-jp/iaas/Content/General/Concepts/servicelimits.htm#iam-service-limits)。

リソース|上限
---|---
テナンシ内のIAMポリシー・オブジェクト|100
IAMポリシー・オブジェクトのIAMポリシー・ステートメント|50
テナンシ内のIAMポリシー・ステートメント|100

一番下の「テナンシ内のIAMポリシー・ステートメント」が話題のやつですね。以下のノートも付いてます。

> テナンシ内のポリシー・ステートメントの500の制限は、増やすことができないハード制限です。ポリシー制限の違反を回避するために、タグベースのアクセス制御(TBAC)を使用することをお薦めします。これにより、1つの文で多数のシナリオに対して1つのポリシーを記述できます。TBACについてさらに学習するには、タグを使用したアクセスの管理を参照してください。

昔のテナントは500以上にできたけど、今は500までだよ、ってことですかね。
100って少なくないです？CloudGuard有効するときに追加されるポリシーが28行。LoggingAnalyticsでも22+6行。本当に100なんかなあ？ 自分のテナントを見たら、過去のごみも含めて100以上あって、試しに追加してみましたけど、追加できました。3年以上前に作ったテナントだから上限が100じゃないってことかもですね。


# 次に使用量の確認

まあ、テナンシ管理の画面で、使用量見えますからね。ここで見れば一目瞭然ですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3781934/17c41295-ec34-4cbc-b53f-bdd03ff2f581.png)

見えないんかーい。

というか、「テナンシ内のIAMポリシー・ステートメント」に該当する項目もないですね。いずれ追加されるのでしょうか。それとも最近作ったテナントには表示されてるんでしょうか。


# ポリシー・ステートメントを減らす方法を考えよう

ここからが本題。


## ポリシー構文でなんとかする

[IAMポリシーの概要](https://docs.oracle.com/ja-jp/iaas/Content/Identity/policieshow/Policy_Basics.htm)はこちら。

```html
Allow <subject> to <verb> <resource-type> in <location> where <conditions>
```

この中で、1行に複数羅列できるものをピックアップしましょう。
* `<subject>`は複数書ける。カンマで繋げれば、複数グループが指定可能。
* `<resource-type>`は一つしか書けないが、`<conditions>`を使って絞ることが可能。

`<subject>`の方は、複数のグループに同じ権限を付与するより、専用のグループを別途作成して、ユーザに複数グループ割り当てた方が分かりやすいんじゃないかなと思います。

`<resource-type>`の方はかなりアクロバティックな方法で、もともと「manage権限は与えるけど、DELETEはできないようにする」みたいな使い方がメインの手順（[参考](https://docs.oracle.com/ja-jp/iaas/Content/Identity/policies/scoping-access-with-permissions-or-api-operations.htm)）なのですが、「全リソースの権限を与えて、`<conditions>`で絞る」ということも一応可能です。

例えば、こちらで書いた[コスト分析](https://qiita.com/sazumi/items/c49365e005d72f1395ca)の場合はこうなります。

```text:修正前
Allow group COST-Admins to manage accountmanagement-family in tenancy
Allow group COST-Admins to manage tickets in tenancy
Allow group COST-Admins to read billing-schedules in tenancy
Allow group COST-Admins to read computed-usages in tenancy
Allow group COST-Admins to read invoices in tenancy
Allow group COST-Admins to read invoice-preferences in tenancy
Allow group COST-Admins to read subscription in tenancy
Allow group COST-Admins to read subscribed-services in tenancy
Allow group COST-Admins to read rate-cards in tenancy
Allow group COST-Admins to read usage-report in tenancy
Allow group COST-Admins to read organizations-entity in tenancy
Allow group COST-Admins to read organizations-subscription in tenancy
Allow group COST-Admins to read organizations-assigned-subscription in tenancy
Allow group COST-Admins to read organizations-subscription-region in tenancy
```

```text:修正後
Allow group COST-Admins to manage accountmanagement-family in tenancy
Allow group COST-Admins to manage tickets in tenancy
Allow group COST-Admins to read usage-report in tenancy
Allow group COST-Admins to read all-resources in tenancy where any { request.permission='BILLING_SCHEDULE_INSPECT', request.permission='BILLING_SCHEDULE_READ', request.permission='COMPUTED_USAGE_INSPECT', request.permission='COMPUTED_USAGE_READ', request.permission='INVOICE_COMPUTED_USAGE_INSPECT', request.permission='INVOICE_READ', request.permission='INVOICE_PREFERENCE_READ', request.permission='SUBSCRIPTION_INFO_READ', request.permission='SUBSCRIBED_SERVICE_INSPECT', request.permission='SUBSCRIBED_SERVICE_READ', request.permission='RATE_CARD_INSPECT', request.permission='RATE_CARD_READ', request.permission='ORGANIZATIONS_ENTITY_INSPECT', request.permission='ORGANIZATIONS_ENTITY_READ', request.permission='ORGANIZATIONS_SUBSCRIPTION_INSPECT', request.permission='ORGANIZATIONS_SUBSCRIPTION_READ', request.permission='ORGANIZATIONS_ASSIGNED_SUBSCRIPTION_INSPECT', request.permission='ORGANIZATIONS_ASSIGNED_SUBSCRIPTION_READ', request.permission='ORGANIZATIONS_SUBSCRIPTION_REGION_INSPECT' } 
```

いやー。最後の行がすごいことになっていますね。`accountmanagement-family`、`tickets`、`usage-report`の細かい権限が調べられなかったので残しましたが、14行が4行になりました。

かなりの力技ですが、一度設定したらほぼ変更しないようなポリシーの場合は有効だと思います。


## タグを使ってなんとかする

上に書きましたが、公式ドキュメントに以下の文があります。

> ポリシー制限の違反を回避するために、タグベースのアクセス制御(TBAC)を使用することをお薦めします。

[タグベースのアクセス制御の説明はこちら](https://docs.oracle.com/ja-jp/iaas/Content/Tagging/Tasks/managingaccesswithtags.htm)。メインの用途はリソースのグルーピングでしょうね。

ポリシー構文の`<location>`がコンパートメントなので、細かくコンパートメントを分けていると、どうしてもコンパートメントごとにステートメントが必要になります。もちろん親子関係だけであれば親のコンパートメントに権限を付与すれば良いですが、`system-A:network`、`system-B:network`みたいな構成で、どちらも同じネットワーク管理者が管理する場合、タグを使えば、一つのステートメントでどちらのコンパートメントも管理できるようになると思います。

**いや、待てよ。**

ドキュメントをよく読んで、こういうのはどうだろうと試してみました。

まず、作成したタグは以下。

ネームスペース名|タグ名|説明
---|---|---
GroupAuthZ|Name|グループに付与する認可名
CompartmentAuthZ|ManageGroup|コンパートメントの管理グループ
〃|UseGroup|コンパートメントの利用グループ
〃|ReadGroup|コンパートメントの参照グループ

作成したポリシーは以下。

```
Allow any-group to inspect groups in tenancy
Allow any-group to inspect compartments in tenancy
Allow any-group to manage all-resources in tenancy where request.principal.group.tag.GroupAuthZ.Name = target.resource.compartment.tag.CompartmentAuthZ.ManageGroup
Allow any-group to use all-resources in tenancy where request.principal.group.tag.GroupAuthZ.Name = target.resource.compartment.tag.CompartmentAuthZ.UseGroup
Allow any-group to read all-resources in tenancy where request.principal.group.tag.GroupAuthZ.Name = target.resource.compartment.tag.CompartmentAuthZ.ReadGroup
```

作成したグループは以下。

グループ名|タグキー|値
---|---|---
AuthDEMO-Admins|GroupAuthZ.Name|AuthDEMO-Admins
AuthDEMO-Users|GroupAuthZ.Name|AuthDEMO-Users
AuthDEMO-Readers|GroupAuthZ.Name|AuthDEMO-Readers

作成したコンパートメントは以下。

コンパートメント名|タグキー|値
---|---|---
AuthDEMO|CompartmentAuthZ.ManageGroup|AuthDEMO-Admins
〃|CompartmentAuthZ.UseGroup|AuthDEMO-Users
〃|CompartmentAuthZ.ReadGroup|AuthDEMO-Readers


上記の状態で、ユーザにグループを割り当ててみたところ、、、想定通り動いてる？
この方法なら、コンパートメントを追加しても、ポリシーの追加が不要になると思います。

まあ、こういう細工をするとあとで分からなくなって大変なんですけど、あまり複雑なことをしなければ大丈夫でしょう。たぶん。


# まとめ

思ったよりは削減できそうですね。
あとは、OCI CLIとか使って全ポリシー引っこ抜いて、重複行を見つけ出して削除するとか、そういう地味な作業が必要なんだろうなと思いました。