Redshift Data API の認証方法のおさらいしながら、アクセスにIP制限を加えてみる

2021.05.20

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

データアナリティクス事業本部の笠原です。

Redshift の Data API、Redshiftクラスタへの接続がめちゃくちゃ簡単になって、便利ですよね。

[アップデート] API で非同期な SQL クエリが実行できる!Amazon Redshift で Data API が利用可能になりました

VPC外からもアクセスできる利便性があるので、実業務で利用する場合はセキュリティ要件を満たすように注意する必要があります。

今回は、特定のIPからのアクセスに制限する必要があったので、Data APIの認証方法をおさらいしながらIP制限方法を調べてみました。

3行まとめ

  • Redshift Data APIの認証方法は、「Secrets Manager」と「一時認証情報」の2種類ある
  • Secrets Manager使うなら secretsmanager:GetSecretValue アクション、一時認証情報使うなら redshift:GetClusterCredentials アクションを許可しておく
  • IP制限したければIAMユーザ / IAMロールにIP制限を加えたIAMポリシーをアタッチすれば良い

Redshift Data API のアクションを確認

以下の公式ドキュメントを確認しておきましょう。

AWSマネージドポリシーでは AmazonRedshiftDataFullAccess が用意されているので、こちらの中身を見てみると一通りわかると思います。

今回は、Redshift Data APIのアクションの許可は以下のようにしておきます。 2021/05時点で用意されているアクションを全て許可しています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "DataAPIPermissions",
            "Action": [
                "redshift-data:ExecuteStatement",
                "redshift-data:CancelStatement",
                "redshift-data:ListStatements",
                "redshift-data:GetStatementResult",
                "redshift-data:DescribeStatement",
                "redshift-data:ListDatabases",
                "redshift-data:ListSchemas",
                "redshift-data:ListTables",
                "redshift-data:DescribeTable"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}

Redshiftクラスタの認証情報の取得

続いて、Redshiftクラスタの認証方法について見てみましょう。 ExecuteStatement など、一部のアクションではRedshiftクラスタの認証情報が必要です。 認証情報はSecrets Managerを使って取得するか、一時認証情報から取得するか、を選ぶことができます。

Secrets Managerを使用する場合

Secrets Managerを使用する場合は、以下の記事に書かれている方法でSecrets ManagerにRedshiftクラスタの認証を保存しておきます。

[アップデート] API で非同期な SQL クエリが実行できる!Amazon Redshift で Data API が利用可能になりました

この記事では AmazonRedshiftDataFullAccess のマネージドポリシーを使っていますが、このポリシーではSecrets Managerで登録したクレデンシャルに RedshiftDataFullAccess というキーのリソースタグだけを許可しているため、別のキーを使いたい場合はポリシーに追記するなり、適宜設定する必要があります。

        {
            "Sid": "SecretsManagerPermissions",
            "Action": [
                "secretsmanager:GetSecretValue"
            ],
            "Effect": "Allow",
            "Resource": "*",
            "Condition": {
                "StringLike": {
                    "secretsmanager:ResourceTag/RedshiftDataFullAccess": "*"
                }
            }
        },

一時認証情報を使用する場合

Secrets Managerを使わない場合は、一時認証情報を使用することになります。一時認証情報を使用する場合は、ポリシーにてredshift:GetClusterCredentials アクションを許可する必要があります。

AmazonRedshiftDataFullAccess のマネージドポリシーでは、以下のようになっており、DBユーザ名が redshift_data_api_user となるユーザのみ許可されている設定です。別のユーザを使いたい場合は、こちらもポリシーに追記するなり、適宜設定する必要があります。

        {
            "Sid": "GetCredentialsForAPIUser",
            "Effect": "Allow",
            "Action": "redshift:GetClusterCredentials",
            "Resource": [
                "arn:aws:redshift:*:*:dbname:*/*",
                "arn:aws:redshift:*:*:dbuser:*/redshift_data_api_user"
            ]
        },

IP制限をかけてアクセスする場合のポリシー

IP制限をかける場合は、以下のドキュメントにあるようにIAMポリシーに追加設定すれば良いです。

AWS: 送信元 IP に基づいて AWS へのアクセスを拒否する - AWS Identity and Access Management

NotIpAddress 条件にて、許可するIPアドレスを指定しましょう。その際 Bool 条件内にて aws:ViaAWSServicefalse と設定しておきます。こうすることでAWSサービスから他のサービスを呼び出す際にアクセス拒否されないようにしておきます。

        {
            "Sid": "DenyIP",
            "Effect": "Deny",
            "Action": "*",
            "Resource": "*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": [
                        "192.168.0.1/32"
                    ]
                },
                "Bool": {"aws:ViaAWSService": "false"}
            }
        }

実際に試してみる

では、実際にIP制限かかるか試してみます。 以下の構成で行いました。

RedshiftはVPC内のプライベートなサブネット内で起動し、セキュリティグループはインバウンドを設定していません。VPCにインターネットゲートウェイもありません。この状況下でもRedshift Data APIを使うことで外部からアクセスすることが可能になります。

また、Redshiftはdc2.largeインスタンスでノード1台の最小構成で検証します。

今回はIAMユーザを作成し、そこに今回のポリシーをアタッチした上で、ローカルPCのCLIからアクセスを試します。 アクセスする際の経路を変えることで、ローカル側のグローバルIPアドレスを変更します。 IAMポリシーで許可するIPと拒否するIPの2通りで試します。

Secrets Managerを使ってアクセス

IAMポリシーは以下のようにしました。なお許可するIPは、実際にはアクセス元のグローバルIPアドレスを指定しています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "DataAPIPermissions",
            "Action": [
                "redshift-data:ExecuteStatement",
                "redshift-data:CancelStatement",
                "redshift-data:ListStatements",
                "redshift-data:GetStatementResult",
                "redshift-data:DescribeStatement",
                "redshift-data:ListDatabases",
                "redshift-data:ListSchemas",
                "redshift-data:ListTables",
                "redshift-data:DescribeTable"
            ],
            "Effect": "Allow",
            "Resource": "*"
        },
        {
            "Sid": "SecretsManagerPermissions",
            "Action": [
                "secretsmanager:GetSecretValue"
            ],
            "Effect": "Allow",
            "Resource": "*",
            "Condition": {
                "StringLike": {
                    "secretsmanager:ResourceTag/RedshiftDataFullAccess": "*"
                }
            }
        },
        {
            "Sid": "DenyIP",
            "Effect": "Deny",
            "Action": "*",
            "Resource": "*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": [
                        "192.168.0.1/32"
                    ]
                },
                "Bool": {"aws:ViaAWSService": "false"}
            }
        }
    ]
}

CLIからSQLクエリを投げてみます。Secrets Managerでアクセスする際は、Secrets ManagerのARNとRedshiftクラスタ名を指定します。

aws redshift-data execute-statement \
  --secret arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:cm-kasahara-redshift-cluster-test-HogeHoge \
  --cluster-identifier cm-kasahara-redshift-cluster-test \
  --database dev \
  --sql "select * from stl_query limit 1"

以下のように応答が返ってきたらOKです。Redshift Data APIは非同期処理なので、クエリの結果は別のコマンドを使って取得する必要がありますが、今回は割愛します。

{
    "ClusterIdentifier": "cm-kasahara-redshift-cluster-test",
    "CreatedAt": "2021-05-18T15:49:06.553000+09:00",
    "Database": "dev",
    "Id": "6056983b-dd41-4e22-9eb5-835cac1ffa92",
    "SecretArn": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:cm-kasahara-redshift-cluster-test-HogeHoge"
}

では、ポリシーで書かれたIP以外でアクセスした場合は以下のように拒否されます。

An error occurred (AccessDeniedException) when calling the ExecuteStatement operation: User: arn:aws:iam::123456789012:user/cm-kasahara.hiroshi-test is not authorized to perform: redshift-data:ExecuteStatement on resource: arn:aws:redshift:ap-northeast-1:1234566789012:cluster:cm-kasahara-redshift-cluster-test with an explicit deny

無事アクセスが拒否できました。

一時認証情報を使ってアクセス

IAMポリシーは以下のようにしました。Secrets Manager利用との違いは、 secretsmanager:GetSecretValue の代わりに redshift:GetClusterCredentials を許可しておきます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "DataAPIPermissions",
            "Action": [
                "redshift-data:ExecuteStatement",
                "redshift-data:CancelStatement",
                "redshift-data:ListStatements",
                "redshift-data:GetStatementResult",
                "redshift-data:DescribeStatement",
                "redshift-data:ListDatabases",
                "redshift-data:ListSchemas",
                "redshift-data:ListTables",
                "redshift-data:DescribeTable"
            ],
            "Effect": "Allow",
            "Resource": "*"
        },
        {
            "Sid": "GetCredentialsForAPIUser",
            "Effect": "Allow",
            "Action": "redshift:GetClusterCredentials",
            "Resource": [
                "arn:aws:redshift:*:*:dbname:*/*",
                "arn:aws:redshift:*:*:dbuser:*/redshift_data_api_user"
            ]
        },
        {
            "Sid": "DenyIP",
            "Effect": "Deny",
            "Action": "*",
            "Resource": "*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": [
                        "192.168.0.1/32"
                    ]
                },
                "Bool": {"aws:ViaAWSService": "false"}
            }
        }
    ]
}

CLIからSQLクエリを投げてみます。一次認証情報でアクセスする際は、Redshiftクラスタ名、DBユーザ名、DB名を指定すれば、アクセス可能です。

aws redshift-data execute-statement \
  --db-user redshift_data_api_user \
  --cluster-identifier cm-kasahara-redshift-cluster-test \
  --database dev \
  --sql "select * from stl_query limit 1"

まとめ

Redshift Data APIは非常に便利な機能です。セキュリティ要件に合わせて、適切な運用を心がけていきましょう。