Aurora MySQLでIAM認証する手順と検証メモ


Aurora MySQLでIAM認証を検証したので、備忘録を兼ねて諸々メモ。

AuroraのIAM認証とは?

DBユーザーに、個別のパスワード入力の代わりにIAMクレデンシャルで認証する機能。
DBごと、ユーザーごとのパスワード管理が不要になる。
スロットルのリスクがあるためアプリケーションの認証には不向きだが、運用に伴う人による認証については一考の価値がある。

Aurora側での設定

  • クラスターでIAM認証を有効化しておく。マネジメントコンソールでいうと、インスタンス設定(≠クラスター設定)の以下が該当設定。
  • データベースで、IAM認証を有効にしたDBユーザーを作成する。IAM認証は、あくまでこのDBユーザーに対して、AWSプロファイルのIAMクレデンシャルでログインする行為であって、DBユーザーがないと機能しない点に注意。
DBユーザーの作成
CREATE USER ssotest IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS';

踏み台インスタンスでの設定

  • DB接続用のmysqlクライアントを事前にインストールしておく。
    • 実は微妙にハマリポイント。Amazon Linux 2で普通にsudo yum install mysqlとすると、MariaDB互換クライアントがインストールされる(Amazon Linux 1だとMySQL純正クライアントが入る)のだが、MariaDB互換クライアントを使う場合、公式ドキュメントに記載のオプションではログインできない(後述の通り、別のオプションを使えば行けるが、そこに辿り着くまで大分かかった)。
    • MySQL純正クライアントをインストールしてもよいが、アークテクチャーを選んでローカルインストールなど若干面倒くさい手順が必要になるので、ここでは一行で済むMariaDB互換をインストールする。
  • IAM認証時は、TLS接続設定に関わらずAuroraのCA証明書が必要になるので、ホームディレクトリあたりにwgetしておく。
    • 少し前はrds-ca-2019-root.pemだったが、いつのまにか変わったらしい。リージョンごとの差異を吸収する
CA証明書の保存
% wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem

権限付与の方針決定

  • DBへのアクセスは一般に、パブリックアクセスは勿論、任意の端末からの接続を禁止するために、VPC内の特定のEC2インスタンス(踏み台)からのアクセスにセキュリティグループで縛ることが多いと思う。そうでないパターンも当然あり得るが、ここでは一応、踏み台パターンを前提とする。
  • IAM認証には接続トークンの生成が必要になるが、踏み台からのIAM認証パターンは以下の二種類が考えられる。違いは、「誰の権限で・どこでトークンを生成するか」の観点。
    • 踏み台のインスタンスプロファイル(ロール)にIAM認証を許可するポリシーを付与。接続トークンは踏み台内で生成して環境変数に格納。
    • 踏み台には権限を付与せず、それ以外のIAMロールにIAM認証を許可するポリシーを付与。接続トークンは別端末で生成してコピペ。
  • 具体的には、以下のようなポリシーをどこのロールに割り当てるかの話になる。
IAM認証ポリシー例
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "rds-db:connect"
            ],
            "Resource": [
                "arn:aws:rds-db:ap-northeast-1:123456789012:dbuser:test-cluster/ssotest"
            ],
            "Condition": {
                "ForAnyValue:StringLike": {
                    "aws:PrincipalOrgPaths": "o-oXXXXXXXXX/r-YYYY/ou-ZZZZZZZZZZZZZ/*"
                }
            }
        }
    ]
}
  • プロコンはざっくり以下のようなイメージ。今回は踏み台の多系統化を避けて「別のIAMロール案」とする。具体的にはAWS SSOを使い、アクセス権限セットにrds-db:connectを付与する。
インスタンスプロファイル案 別のIAMロールに付与する案
利点 認証操作がシンプル 踏み台は一系統でよい
欠点 権限ごとに踏み台を分岐する必要がある トークンを持ち込む必要がありひと手間かかる

接続

  • まずは認証トークン取得。前述の方針に従い、今回はAWS SSOでログイン後、踏み台の外でトークンを取得する(踏み台でトークン生成コマンドを叩くと、踏み台のインスタンスプロファイルの権限内で生成され、今回だとどこにも接続できないことになるため)。
認証トークン取得
% aws sso login --profile ssotest-profile
% aws rds generate-db-auth-token \
> --hostname hoge \
> --region ap-northeast-1 \
> --port 3306 \
> --username ssotest \
> --profile ssotest-profile
  • トークンが生成されたら、クリップボードにコピーする。本来は環境変数に格納するのが楽なのだが、今回は踏み台に持ち込む必要上、コピペで対応する。
  • 踏み台にSSM Sessions Manager等でログイン後、DB接続を実行する。ここではMariaDB互換クライアントのパターンを使用するが、参考までにMySQL純正クライアントのパターンも併記しておく。
DB接続(MariaDB互換クライアントのパターン)
% TOKEN='(先程取得したトークンの文字列をそのままコピペ)'
% mysql -h <Auroraエンドポイント>.ap-northeast-1.rds.amazonaws.com \
> --port=3306 \
> --user=ssotest \
> --password=$TOKEN \
> --ssl-ca=~/global-bundle.pem \
> --default-auth=mysql_clear_password
DB接続(MySQL互換クライアントのパターン)
% mysql -h <Auroraエンドポイント>.ap-northeast-1.rds.amazonaws.com \
> --port=3306 \
> --user=ssotest \
> --password=$TOKEN \
> --ssl-ca=~/global-bundle.pem \
> --enable-cleartext-plugin

無事繋がったら完了。
なかなかMySQLのプロンプトが出ないな?という場合は、セキュリティグループを見直してみることをお薦めする。ちゃんと3306が許可されてるかとか、Aurora側でEC2側セキュリティグループをIngressに含めているか、など。
Access Deniedの際は以下のようなエラーが即時返ってくるので、そうでない場合はまずネットワークを疑う。

AccessDeniedな場合の接続エラー
ERROR 1045 (28000): Access denied for user 'ssotest'@'XXX.XXX.XXX.XXX' (using password: YES)

小ネタ

  • トークンは15分だけ有効。
    • 一度生成したらいつでもどこでも再利用できるわけではなく、一定期間で切れるようになっている。
  • 別アカウントで取得したトークンは使えない。
    • IAMポリシーがどうあれ、アカウントをまたいでトークンを再利用することはできない。例えば、開発アカウントでたまたまrds-db:connect権限を持っていてトークンを生成できたとしても、それを使って本番アカウントのDBにログインすることができないようになっている。
  • 「インスタンスプロファイル案」を選択して踏み台からトークンを生成する場合、当たり前だが、RDSのAPIエンドポイントへの経路が必要になる。
    • 踏み台のEgressをガチガチに縛っている場合は、VPCエンドポイントが必要になるかも。
  • AdministratorAccessなど、iam:*を保持している権限だと、ポリシー内容に拘わらず全部接続できてしまう。
    • 検証する際は注意しておかないと、想定した挙動にならなくて時間を無駄にする。
  • AWS SSOでも使える。
    • 今は改修されているが、以前は公式ドキュメントに「DBユーザー名はロール名と同じにせよ」という誤記があり、ロール名にアカウントごとのランダム文字列が付記されるAWS SSOでは運用上成立しないように見えていたが、原文は"Make sure the specified database user name is the same as a resource in the IAM policy for IAM database access."なので単なる誤訳だったらしい。
  • Resourceの部分はワイルドカードを使える。つまり、
Resource句(個別指定)
            "Resource": [
                "arn:aws:rds-db:ap-northeast-1:123456789012:dbuser:test-cluster/ssotest"
            ],

は、要件に応じて、以下のようにも書き変えることができる。

Resource句(ワイルドカード)
            "Resource": [
                "arn:aws:rds-db:*:*:dbuser:*/ssotest"
            ],

ただし、このままだといささか範囲が広すぎるので、せめてOrganizationsで縛るくらいはしておくこと。要件によっては、対象アカウントを本番とそれ以外に分けたり、対象クラスターを分けたりとかもあるかも知れない。

Condition句(OU縛り)
            "Condition": {
                "ForAnyValue:StringLike": {
                    "aws:PrincipalOrgPaths": "o-oXXXXXXXXX/r-YYYY/ou-ZZZZZZZZZZZZZ/*"
                }
            }