Retool で AssumeRole してDynamoDBテーブルにアクセスする(アクセスキーが流出しても、被害を最小限にしよう)

RetoolでDynamoDBを使うときは、AssumeRoleをしましょう。少しの手間で安全性が向上し、精神衛生上も気が楽になります。
2021.08.12

いろんなデータベースやAPIと接続してWebアプリをサクッと作れるRetoolですが、DynamoDBと連携する場合は、IAMユーザのアクセスキーを使います。 もし、アクセスキーが流出したら一大事です。 そこで、AssumeRoleを使って、少しでも影響が小さくなる方法を試してみました。

Retoolについては、下記をご覧ください。

おすすめの方

  • RetoolでAssumeRoleしたい方

IAMユーザとIAMロールと実験用のDynamoDBテーブルを作成する

CloudFormationテンプレート

下記を作成します。

  • Retool用のIAMユーザ
  • Retool用のIAMユーザに付与するIAMポリシー(AssumeRoleのみ可能)
  • Retool用のIAMユーザがAssumeRoleするIAMロール(DynamoDBに対する操作権限のみ)
  • DynamoDBテーブル

AssumeRoleを活用すると、「Retool用のIAMユーザ」のアクセスキーが流出しても、AssumeRoleしかできません。 また、AssumeRoleする条件として「RetoolのIPアドレスのみ許可」しています。 そのため、第三者がアクセスキーを使っても、Retool以外はAssumeRoleできず、結果としてDynamoDBにアクセスできません。

aws.yaml

AWSTemplateFormatVersion: 2010-09-09
Description: IAM and DynamoDB for Retool

Resources:
  # Retool用のIAMユーザ
  RetoolUser:
    Type: AWS::IAM::User
    Properties:
      UserName: retool-user

  # Retool用のIAMユーザに付与するIAMポリシー(AssumeRoleのみ可能)
  # -> もし、アクセスキーが流出しても、AssumeRoleしかできない
  RetoolUserPoricy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: retool-user-policy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Action: "sts:AssumeRole"
            Resource: !GetAtt RetoolRole.Arn
      Users:
        - !Ref RetoolUser

  # Retool用のIAMユーザがAssumeRoleするIAMロール(DynamoDBに対する操作権限のみ)
  RetoolRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: retool-user-role
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Action: "sts:AssumeRole"
            Principal:
              AWS:
                - !GetAtt RetoolUser.Arn
            Condition:
              IpAddress:
                aws:SourceIp:
                  # Retoolからのアクセスのみ、AssumeRoleできる
                  # IP Addresss -> https://docs.retool.com/docs/connecting-your-database
                  - 13.80.4.170/32
                  - 13.93.15.89/32
                  - 13.93.15.84/32
                  - 13.76.231.249/32
                  - 13.76.97.52/32
                  - 13.76.194.227/32
                  - 52.177.12.28/32
                  - 52.177.12.26/32
                  - 52.177.118.220/32
                  - 52.175.194.171/32
                  - 52.175.251.223/32
                  - 52.247.195.225/32
      Policies:
        - PolicyName: retool-user-role-policy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "dynamodb:ListTables"
                Resource: "*"
              - Effect: "Allow"
                Action:
                  - "dynamodb:DeleteItem"
                  - "dynamodb:ListTagsOfResource"
                  - "dynamodb:DescribeReservedCapacityOfferings"
                  - "dynamodb:DescribeTable"
                  - "dynamodb:GetItem"
                  - "dynamodb:DescribeContinuousBackups"
                  - "dynamodb:DescribeLimits"
                  - "dynamodb:BatchGetItem"
                  - "dynamodb:BatchWriteItem"
                  - "dynamodb:PutItem"
                  - "dynamodb:ListBackups"
                  - "dynamodb:Scan"
                  - "dynamodb:Query"
                  - "dynamodb:DescribeStream"
                  - "dynamodb:UpdateItem"
                  - "dynamodb:DescribeTimeToLive"
                  - "dynamodb:ListStreams"
                  - "dynamodb:DescribeGlobalTableSettings"
                  - "dynamodb:ListGlobalTables"
                  - "dynamodb:GetShardIterator"
                  - "dynamodb:DescribeGlobalTable"
                  - "dynamodb:DescribeReservedCapacity"
                  - "dynamodb:DescribeBackup"
                  - "dynamodb:GetRecords"
                Resource: !GetAtt RetoolDeviceTable.Arn
      MaxSessionDuration: 3600

  # DynamoDBテーブル
  RetoolDeviceTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: retool-device-table
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: deviceId
          AttributeType: S
      KeySchema:
        - AttributeName: deviceId
          KeyType: HASH

Outputs:
  RetoolRoleArn:
    Value: !GetAtt RetoolRole.Arn

Retoolで操作するDynamoDBテーブルが決まっている場合、dynamodb:ListTables以外は、ResourceにDynamoDBテーブルのARNを指定するとさらに安全になります。

デプロイ

aws cloudformation deploy \
    --template-file aws.yaml \
    --stack-name IAM-and-Retool-Sample-Stack \
    --capabilities CAPABILITY_NAMED_IAM

アクセスキー取得

aws iam create-access-key \
    --user-name retool-user

IAMロールのARNを取得

aws cloudformation describe-stacks \
    --stack-name IAM-and-Retool-Sample-Stack \
    --query 'Stacks[].Outputs'

手元のPCで、AssumeRoleできないことを確認する

AsssumeRoleの条件に「RetoolのIPアドレス」を指定しているので、手元のPCからAssumeRoleできないことを確認します。

アクセスキーを環境変数に登録する

さきほど取得したアクセスキーを環境変数に設定します。

export AWS_ACCESS_KEY_ID=xxx
export AWS_SECRET_ACCESS_KEY=yyy
export AWS_DEFAULT_REGION=ap-northeast-1

この状態だと、DynamoDBテーブルにアクセスできない

Retool用のIAMユーザには、AssumeRoleする権限のみを付与しているため、DynamoDBテーブルにアクセスできません。

$ aws dynamodb list-tables

An error occurred (AccessDeniedException) when calling the ListTables operation: User: arn:aws:iam::000000000000:user/retool-user is not authorized to perform: dynamodb:ListTables on resource: arn:aws:dynamodb:ap-northeast-1:000000000000:table/*

AssumeRoleもできない

さきほど取得した「AssumeRole用のARN」を指定します。許可されていないIPアドレスなので、AssumeRoleできません。

$ aws sts assume-role \
	--role-arn arn:aws:iam::000000000000:role/retool-user-role \
	--role-session-name local-test \
    --duration-seconds 900

An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:iam::000000000000:user/retool-user is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::000000000000:role/retool-user-role

Retoolのリソースを作成する

必要事項を入力して、DynamoDBに対するリソースを作成します。

「Role to assume (ARN)」が未入力の場合、「Test Connection」が失敗します。

アクセス失敗する

「Role to assume (ARN)」を入力すると、接続成功しました。

アクセスに成功した

Retoolでデータ取得を確認する

適当なデータをDynamoDBテーブルに格納する

DynamoDBテーブルに適当なデータを追加した

RetoolのアプリでDynamoDBテーブルのデータを確認する

以前に作成したアプリを流用します。

TableのResourceTableをさきほど作成したリソースとDynamoDBテーブルに変更します。 Methodscanを指定します。

Retoolのリソース設定を変更する

「Save & Run」を選択すると、無事にデータ取得できました!

DynamoDBテーブルのデータ取得に成功した

異なるDynamoDBテーブルには、アクセスできない

アクセスを許可していないDynamoDBテーブルの中身は、表示できません。期待通りですね。

DynamoDBテーブルのデータ取得に失敗した

さいごに

少し手間かもしれませんが、安全性が格段に向上します。 精神衛生上も気が楽になりますので、おすすめです。

参考