🐈

Amazon Verified PermissionsをつかったLambda Authorizerを試してみた

2023/07/17に公開

はじめに

2023年6月23日にGAしたAmazon Verified Permissionsを利用してLambda Authorizerを試してみました.ソースコードはこちら.
https://github.com/manaty226/lambda-authorizer-verified-permissions

システム構成

お試しなのでAPI GatewayとLambda Authorizerだけつくって,APIの応答はAPI GatewayのMockで返すことにしています.

想定ユースケース

以前SpiceDBによる認可制御でやったようなブログサービスを想定して,REST APIにより以下の操作を実現します.

操作 HTTPメソッド 認可権限ポリシー
ブログの取得 GET 全ての人が実行可能
ブログの編集 PUT ブログのオーナーのみ実行可能

普通のサービスならブログの作成(POST)や削除(DELETE)もサポートすると思いますが,認可判断デモに必要な最小限の操作だけ考えます.

Verified Permissionsの設定

Verified Permissionsにおける認可概念

Verified Permissionsの認可判断にはプリンシパル,リソース,アクションという3つの要素が中心になり,「誰が」,「何に」,「何を」できるかを判断します.はじめにプリンシパルは「誰が」に対応するもので,今回の例だとREST APIを叩くユーザと想定しています.次にリソースは「何に」に対応するもので,今回はブログです.最後にアクションは「何を」に対応して,今回は取得と編集になります.Verified Permissionsの設定ではポリシーストアを作成する際にポリシースキーマによってプリンシパル,リソース.アクションの器を作っておき,ポリシーの作成でそれらをつかってどのような認可判断をおこなうか記述します.

ポリシースキーマ

Verified Permissionsをはじめるには最初にポリシーストアを作成する必要があります.
具体的な方法は公式ブログにも載っているので割愛して,ここでは上のユースケースを実現するために以下のようなエンティティとアクションを定義しました.また,ポリシースキーマを表すjsonも記載しておきます.

名前 種別 意味
Blog エンティティ ブログを示すエンティティ
User エンティティ ユーザを示すエンティティ
Read アクション ブログの取得操作を示すアクション
Write アクション ブログの編集操作を示すアクション
スキーマ
{
    "MyBlogApp": {
        "actions": {
            "Read": {
                "appliesTo": {
                    "resourceTypes": [
                        "Blog"
                    ],
                    "principalTypes": [
                        "User"
                    ]
                }
            },
            "Write": {
                "appliesTo": {
                    "resourceTypes": [
                        "Blog"
                    ],
                    "principalTypes": [
                        "User"
                    ]
                }
            }
        },
        "entityTypes": {
            "Blog": {
                "memberOfTypes": [],
                "shape": {
                    "attributes": {
                        "owner": {
                            "type": "String",
                            "required": true
                        }
                    },
                    "type": "Record"
                }
            },
            "User": {
                "shape": {
                    "type": "Record",
                    "attributes": {
                        "sub": {
                            "required": true,
                            "type": "String"
                        }
                    }
                }
            }
        }
    }
}

ポリシー設定

ポリシーストアを作ったあとは具体的な認可ポリシーを作成します.
今回は次の2つのポリシーを作成しました.

# 内容
1 全てのリソースに対して全てのプリンシパルからのReadアクションが許可される
2 リソースのオーナーは自分のリソースに対してReadおよびWriteのアクションが許可される

No.1に対応するポリシーは以下のように記述できます.

permit (
    principal,
    action in [MyBlogApp::Action::"Read"],
    resource
)
when { true };

また,No.2に対応するポリシーは以下のように記述できます.

permit(principal, action, resource)
when {
    principal has sub && resource has owner &&
    principal.sub == resource.owner
};

これらのポリシーはCedarとよばれるポリシー記述言語によって表現されます.
この2つのポリシー記述から分かるとおり,RBAC(Role Based Access Control)やIBAC(Identifier Based Access Control)を表現するpermit句と,ABAC(Attribute Based Access Control)を表現するwhen句で構成されます.この2つの句によって簡単かつ柔軟なポリシーを生成することができます.

Cognitoとの連携

Verified PermissionsはCognitoユーザプールと連携し,Cognitoが発行したアクセストークンやIDトークンを検証し,クレームの情報をもとにポリシー評価することができます.後述しますが基本的にIDトークンを渡すことが前提のようで,アクセストークンを使った評価は限定的です.

Lambda AuthorizerとVerified Permissionsによる認可制御

Lambdaの処理概要

Lambda AuthorizerはAPI Gatewayを介してヘッダーのべアラトークンを受け取りプリンシパルにマッピングし,リクエストパスからリソースを,HTTPメソッドからアクションをマッピングしてVerified Permissionsに認可判断を依頼します.該当のコードはこのあたり.
https://github.com/manaty226/lambda-authorizer-verified-permissions/blob/3fc2ffffddc683c394475a169b7f3088c2ebde12/lambda/authorizer/src/index.py#L12-L22

Verified Permissionsを使った認可判断はboto3ライブラリを使って実施します.
このあたりのパラメータもAWSの公式ドキュメントにありますが,resourceとentitiesの違いなど結構難しく感じました...

https://github.com/manaty226/lambda-authorizer-verified-permissions/blob/3fc2ffffddc683c394475a169b7f3088c2ebde12/lambda/authorizer/src/authorizer.py#L20-L45

実行結果

Cognitoから取得したトークンをヘッダーに付与してREST APIを呼び出すとブログの読み取り操作結果として以下の応答が得られます.

$ curl https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/blogs/1
-H 'authorization: Bearer xxxxx
{
 "title": "Title of blog",
 "content": "This is test blog"
 }

ブログの編集操作はblos/1のみ許可されます.

$ curl -X PUT https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/blogs/1
-H 'authorization: Bearer xxxxx
-d 
{
 "content": "This is test blog"
}
{
 "message": "blog update scceeded." 
}
$ curl -X PUT https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/blogs/2
-H 'authorization: Bearer xxxxx
-d 
{
 "content": "This is test blog"
}
{"Message":"User is not authorized to access this resource with an explicit deny"}

Verified Permissionsを使った感想

ポリシーの注意点と疑問点

ポリシーリソースにワイルドカードが使えない

ポリシーストアで表現する特定のリソースセット全てに許可を与えたいようなユースケースがあると思いますが,現在のVerified Permissionsでは以下のようなワイルドカードを使ったポリシーに対応していません.

permit(
  principal,
  action in [MyBlogApp::Action::"Read"],
  resource == MyBlogApp::Blog::"*"
) when {
  true
};

ABAC表現時の書き方

前述したようにポリシーはwhen句によって属性をつかった認可判断に拡張することができますが,ポリシー評価時に属性が与えられていない(entitiesに属性を与えられていない)とエラーになってしまいます.例えば今回の例だと,Read権限の評価はポリシー#1と#2の両方が評価されますので,公式ドキュメントのサンプルリクエストのような投げ方をするとエラーが返ってきます.そのため,次のように書くとよさそうです.

permit(principal, action, resource)
when {
    principal has sub && resource has owner && //hasをつかって属性を保有しているか判断する
    principal.sub == resource.owner
};

ドキュメント

Verified PermissionsはGAされたばかりのためかとにかくドキュメントが少ないです.この方が書いているように,1ヶ月ほど経った今もやってみたブログがほぼないので,公式ブログやWorkshop資料などを頼りに書く必要がありますが,全ての概念を十分カバーしているわけではないので,よくわからないことが多いです.例えば,リソースに親子の概念を作ることができますが,それがポリシー評価においてどのように有効に利用できるのかいまいちよくわかりませんでした.

周辺エコシステム

GAされたばかりのため,周辺エコシステムもまだ揃っていないように思います.例えばTerraformも現在対応中です.
https://github.com/hashicorp/terraform-provider-aws/issues/32158

外部のエコシステムだけでなく,AWSの他のサービスとの連携もまだこれからのように思います.例えば,Lambdaのboto3ライブラリのバージョンは1.26.90であり,Verified PermissionsのSDKが入っていません(2023年7月17日時点).そのため,最新のboto3ライブラリのlayerを作って一緒にデプロイする必要があります.
また,Cognitoと連携してトークンのクレーム値を利用することができますが,Cognitoの仕様制約でIDトークンしかカスタムクレームを追加することができません.基本的にOIDCの文脈ではIDトークンを認証成功時に自身のバックエンドに投げる以外サーバに投げることはなくアクセストークンを使うので,Cognitoの仕様制約によりVerified Permissionsの良さがいくらか減少しているように思います.

おわりに

Verified Permissionsをつかった認可制御するLambda Authorizerを試してみました.ポリシー記述によってRBACやABACなど様々な認可権限を表現できそうです.一方で,まだまだGAして間もないためエコシステムやポリシー表現の成熟が必要です.また,ポリシー判断に必要な情報を全て詰め込んでリクエストしないといけないので,SpiceDBのようにユーザの識別子だけ渡せばよしなに認可判断してくれるほうがゲートウェイのAuthorizerとしては都合がいいなとも感じました.

Discussion