AWS IAM SDKは、Cognitoグループロールを使用するときに特定のロールのすべてのポリシードキュメントを取得します

Aug 17 2020

AWS Cognitoユーザープールグループを使用して、API GatewayAPIのアクセス許可を管理しています。ドキュメントに記載されているように、これはグループの有効な使用法であると私は信じています。https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-user-groups.html#using-groups-to-control-permission-with-amazon-api-gateway。

残念ながら、私が知る限り、このユースケースのドキュメントは基本的に存在しません(その小さな段落は別として)。これがカスタムAPIGatewayオーソライザーLambda関数でどのように機能するかを理解しようとしています。テストロールを作成し、これをCognitoのテストグループに割り当てました。ロールには単一のポリシーが添付されていますが、将来的にはロールに複数のポリシーが設定されます。

現在、カスタムオーソライザーでアクセストークンなどを検証していますが、すべて正常に機能しています。私は現在、グループ/ロール/ポリシーを使用する際に、このきめ細かいアクセス制御を追加しようとしています。IAM SDKをインストールし、どの呼び出しを行う必要があるかを調べています。役割とそのすべてのポリシーを取得する簡単な方法はないようです。私が思いついた最高のものは次のとおりです。

public async Task<IEnumerable<string>> GetGroupPermissionsForUserAsync(Models.User user)
{
    if (!user.UserAttributes.TryGetValue(UserAttributeName.UserPoolId, out var userPoolId))
    {
        return null;
    }

    var groups = await GetUserGroups(user.Username, userPoolId);
    var groupRoleArn = groups.FirstOrDefault()?.RoleArn;
    var policies = new List<string>();
    if (!string.IsNullOrWhiteSpace(groupRoleArn))
    {
        var roleName = groupRoleArn.Substring(groupRoleArn.IndexOf('/') + 1);
        var rolePoliciesResponse = await _iamClient.ListAttachedRolePoliciesAsync(new ListAttachedRolePoliciesRequest { RoleName = roleName });
        
        foreach (var rolePolicy in rolePoliciesResponse.AttachedPolicies)
        {
            var policyVersionsResponse = await _iamClient.ListPolicyVersionsAsync(new ListPolicyVersionsRequest
            {
                PolicyArn = rolePolicy.PolicyArn
            });

            var latestPolicyVerson = policyVersionsResponse.Versions.OrderByDescending(x => x.CreateDate).LastOrDefault();
            var policyVersionResponse = await _iamClient.GetPolicyVersionAsync(new GetPolicyVersionRequest
            {
                PolicyArn = rolePolicy.PolicyArn,
                VersionId = latestPolicyVerson.VersionId
            });
            
            if (!string.IsNullOrWhiteSpace(policyVersionResponse?.PolicyVersion.Document))
            {
                policies.Add(HttpUtility.UrlDecode(policyVersionResponse.PolicyVersion.Document));
            }
        }
    }

    return policies;
}

private async Task<IEnumerable<GroupType>> GetUserGroups(string username, string userPoolId)
{
    string nextToken = null;
    var groups = new List<GroupType>();
    do
    {
        var response = await _cognitoClient.AdminListGroupsForUserAsync(new AdminListGroupsForUserRequest
        {
            Username = username,
            UserPoolId = userPoolId
        });

        groups.AddRange(response.Groups);

        nextToken = response.NextToken;
    } while (!string.IsNullOrWhiteSpace(nextToken));

    return groups.OrderBy(x => x.Precedence);
}

ご覧のとおり、ロールのポリシーを取得するためだけに、たくさんの電話をかける必要があります。

  1. _cognitoClient.AdminListGroupsForUserAsync Cognitoからユーザーのグループを取得します。
  2. _iamClient.ListAttachedRolePoliciesAsync ロールに関連付けられたポリシーを取得します。
  3. _iamClient.ListPolicyVersionsAsync 各ポリシーのバージョンを取得します。
  4. _iamClient.GetPolicyVersionAsync 最終的にポリシードキュメントを持つ個々のポリシーバージョンを取得します。

ListPolicyVersionsAsyncドキュメントプロパティを含む応答を返しますが、何らかの理由で常にnullであるため、追加のGetPolicyVersionAsync呼び出しが必要です。

これはすべてレイテンシーを追加するだけでなく(APIへのすべての呼び出しがこのコードを介して実行されるオーソライザー関数の問題です)、何らかの方法で重複排除する必要がある個々のポリシーの束だけが残りますAPIGatewayに戻る前。

これを行うためのより簡単で速い方法は本当にありませんか?より少ない呼び出しでこのすべての情報を取得する方法はありますか?また、ルールが重複する場合にポリシーをフラット化する方法はありますか?

回答

lemming Aug 24 2020 at 17:00

CognitoとAPIGatewayを使用して承認を設定するにはさまざまな方法があるため、API Gatewayメソッドを介してアクセスしようとしているAWSリソースがわからないと、何が最適かを判断するのは困難です。たとえば、API Gatewayメソッドはデータベース情報にアクセスするラムダメソッドと統合されていますか、それともサービスを直接呼び出していますか?

私が正しく理解している場合は、ユーザーが属する可能性のある(潜在的に)複数のグループのアクセス許可を組み合わせて、その特定の要求に使用する単一のアクセス許可のセットを考え出す必要があります。したがって、userロールはユーザーデータへのアクセスを許可する可能性があり、adminロールは他の管理エンドポイントへのアクセスをさらに拡張する可能性がありますか?

API Gatewayメソッドがラムダメソッドと統合され、データベースなどの基盤となるAWSリソースにアクセスするというかなり標準的なシナリオがあるとすると、次のようにカスタムオーソライザーを使用できます。

カスタムオーソライザーが、要求されたAPIメソッドを実行するために必要なアクセス許可を返すだけの場合は、作業が大幅に簡素化されます。

{
  "principalId": "sub-from-ID-token",
  "policyDocument": {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Action": "execute-api:Invoke",
        "Effect": "Allow",
        "Resource": [
          "arn:aws:execute-api:eu-west-1:1234567890:qwerty:prod:GET/user/address"
        ]
      }
    ]
  }
}

標準的なシナリオでは、正常に承認されたリクエストは、APIロジックを含むラムダを実行します。このバッキングラムダには、DynamoDBテーブルのデータなどの保護されたリソースにアクセスするためのすべてのアクセス許可を定義する実行ロールがあります。したがって、バッキングラムダが別のAWSリソースにアクセスするときはいつでも、次のアクセス許可を更新する必要はありません。いくつかのグループロールがありますが、代わりに、システム内の1つの場所でそれらのアクセス許可を管理します。

この設定では、保護が必要なAPIの領域ごとに1つの非常に単純なカスタム認証機能を使用できます。

たとえば、/user/*エンドポイントを保護するために、オーソライザーに渡されたIDトークンのクレームにuserグループが含まれているかどうかを確認するだけのカスタムオーソライザーを1つ持つことができますcognito:groups。含まれている場合は、要求されたapiメソッドを実行するために必要なアクセス許可を返します(execute-api:Invoke)。次に/admin/*adminグループがIDトークンのcognito:groups要求に含まれているかどうかを確認するなどして、ルートを保護する別のカスタム承認者がいる場合があります。

カスタム承認者の応答のキャッシュ

各カスタムオーソライザーにはオプションのTTL設定があり、(特定のJWTトークンの)応答がキャッシュされる期間を決定します。これは、ラムダのウォームアップ時間、またはオーソライザーの追加のコールアウトにかかる時間を削減するのに役立ちます。

承認者がいくつかの方法を前面に出している場合、例:

  • GET / user / address
  • POST / user / address
  • GET / user / mobile

次に、正常に承認された応答は、キャッシュがタイムアウトするまで、これらすべてのメソッドに対してキャッシュされることに注意することが重要です。したがって、カスタム承認者によって返されるポリシーは、ユーザーが実行できるすべてのメソッドを定義するリソースのリストを返すか、ワイルドカードを使用する必要があります。

すべての/user/*ルートをカバーする承認者の応答例

{
  "principalId": "sub-from-ID-token",
  "policyDocument": {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Action": "execute-api:Invoke",
        "Effect": "Allow",
        "Resource": [
          "arn:aws:execute-api:eu-west-1:1234567890:qwerty:prod:GET/user/address",
          "arn:aws:execute-api:eu-west-1:1234567890:qwerty:prod:POST/user/address",
          "arn:aws:execute-api:eu-west-1:1234567890:qwerty:prod:GET/user/mobile"
        ]
      }
    ]
  }
}

すべての/user/*ルートをカバーする承認者に対する応答の例(ワイルドカードを使用)

{
  "principalId": "sub-from-ID-token",
  "policyDocument": {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Action": "execute-api:Invoke",
        "Effect": "Allow",
        "Resource": [
          "arn:aws:execute-api:eu-west-1:1234567890:qwerty:prod:*/user/*"
        ]
      }
    ]
  }
}