RedashでAthenaをデータソースにする際に接続をアクセスキーからIAM RoleArnを使ったAssumeRoleに切り替えてみた

RedashでAthenaをデータソースとする際に専用のアクセスキーを発行していましたが、実装を辿ってみるとIAM Roleによるスイッチを選択肢に出来ることが分かり、実際にやってみました。
2021.07.08

RedashのデータソースとしてAthenaを指定する場合、アクセスキーの指定が求められます。ただ、アクセスキー漏洩を考慮して極力IAM Roleへのスイッチしたいのが本音です。

どうにかしてRoleArnを使えないだろうかとソースコードを追ってみたところ、ドキュメント上には存在しないものの、とある環境変数を指定しておくとアクセスキーの欄がIAM RoleArnの入力欄に切り替わる仕組みであることを確認しました。

実際の設定と、画面の違いについてまとめました。

環境変数に指定する

必要なキーは ATHENA_ASSUME_ROLE です。設定する値は何でも構いません。

export ATHENA_ASSUME_ROLE=1

cdkでFargateを使う場合はContainerの environment に追加するだけです。

IAM RoleArnのフォームへ切り替える

環境変数に設定されていれば自動で変わります。変更前後は以下の通り。

変更前 変更後

IAM Role to AssumeにRoleArnを指定し、External ID to be used while STS assume roleにはredash等の固有の文字列を入れておきましょう。

実装について

該当するdefaultブランチ(CommitHash:f21f7e2)でのコードです。環境変数があった場合はRoleArnの取得フォームへと切り替えて、Glueからスキーマを取得する際に認証しています。

ASSUME_ROLE = parse_boolean(os.environ.get("ATHENA_ASSUME_ROLE", "false"))

redash/athena.py at f21f7e211ff5fe12c850a9627cfadb80a5a8819e · getredash/redash - https://github.com/

        if ASSUME_ROLE:
            del schema["properties"]["aws_access_key"]
            del schema["properties"]["aws_secret_key"]
            schema["secret"] = []


            schema["order"].insert(1, "iam_role")
            schema["order"].insert(2, "external_id")
            schema["properties"].update(
                {
                    "iam_role": {"type": "string", "title": "IAM role to assume"},
                    "external_id": {
                        "type": "string",
                        "title": "External ID to be used while STS assume role",
                    },
                }
            )
        else:
            schema["order"].insert(1, "aws_access_key")
            schema["order"].insert(2, "aws_secret_key")

redash/athena.py at f21f7e211ff5fe12c850a9627cfadb80a5a8819e · getredash/redash - https://github.com/

    def _get_iam_credentials(self, user=None):
        if ASSUME_ROLE:
            role_session_name = "redash" if user is None else user.email
            sts = boto3.client("sts")
            creds = sts.assume_role(
                RoleArn=self.configuration.get("iam_role"),
                RoleSessionName=role_session_name,
                ExternalId=self.configuration.get("external_id"),
            )
            return {
                "aws_access_key_id": creds["Credentials"]["AccessKeyId"],
                "aws_secret_access_key": creds["Credentials"]["SecretAccessKey"],
                "aws_session_token": creds["Credentials"]["SessionToken"],
                "region_name": self.configuration["region"],
            }
        else:

redash/athena.py at f21f7e211ff5fe12c850a9627cfadb80a5a8819e · getredash/redash - https://github.com/

    def __get_schema_from_glue(self):
        client = boto3.client("glue", **self._get_iam_credentials())
        schema = {}

redash/athena.py at f21f7e211ff5fe12c850a9627cfadb80a5a8819e · getredash/redash - https://github.com/

実装自体はシンプルですが、追ってみないと気が付かない代物でした。

なお、初回スキーマ取得時には認証に時間が掛かっているのか読み込めず、再度スキーマを取得してみると正常に読み込まれました。

あとがき

RedashからAthenaへ接続するためだけにアクセスキーを発行したくない場合にはとても助かる実装になっています。IAM UserからIAM Roleへの切り替えが求められる状況が故にRedashが選択外になっていた場合にはおすすめです。