【全世界待望】Public AccessのRDSにIAM認証でLambda Pythonから接続する

こんにちは。てるい@さっぽろです。

先日、Serverlessおじさん担当なるものに任命されました。かねてからトイレIoTの元祖として名を馳せ、イケてるIoT事例を連発しているIoTお兄さん担当に負けないよう頑張っていきたいと思います。

さて、先日(2017.04.25)に全世界待望のRDSへのIAM認証(IAM Database Authentication)がリリースされました。リリース時にはSDKのIAM認証を行うための署名を作る機能のリリースが後追いとなっており試すに試せない状況でしたが、今朝(2017.04.27)についにリリースされたので、さっそく検証してみようと思います。

全世界待望?

「LambdaからRDSに繋ぎたいと思ったことはありませんか?」

全世界はかなり言い過ぎですが、ある程度の規模の開発をServerless(API Gateway + Lambda)で行ったことがある人ならば、多くの人が思ったことがあるはずです。ですが、これには今まで大きな壁がありました。

それは、「VPC Lambda 10秒の壁」と呼ばれています(私の中で)

VPC Lambda 10秒の壁

LambdaからRDSへセキュアに接続しようとすると、Private接続を行うためにRDSと同じVPC内でLambdaを起動することがまず最初の選択肢となります。この時、VPC外でコールドスタートしたLambdaには起動する際にVPC内で通信するための「ENI生成」という処理が始めに実行されます。この処理は10秒程度(あるいはそれ以上)かかるものであり、これはオンライン処理では到底許容できるものではありません。

もう一つの選択肢として、RDSをPublic Accessで起動することが挙げられます。ですが、これにはセキュリティ上のリスクが発生します。VPC内でNAT GatewayへRoutingされたLambdaを除いて、接続元のIPアドレスは不定になります。そのため、接続元を公開されているAWSのIPアドレスのみに絞る程度のことしかできません。SSL接続を行うことで盗聴のリスクは回避できますが、認証がMySQLのID/PasswordではSSHをIP制限せずに公開鍵ではなく、Password認証で運用するようなものでした。

IAM認証によってどうなるか

後者のPublic Access時の認証強度の問題が解決できます。つまり、セキュリティを保ったまま10秒の壁が無いLambda FunctionからRDSへ接続することが可能となるということです。

試してみる

さて、前置きはこれくらいにして早速試してみましょう。今回はAWS CLIから操作してみます。途中、ホスト名やパスワードが出てきますが適宜置き換えてください(該当のリソースは既に削除しているのでここに書かれているホスト名やパスワードではどこにも繋がりません)

RDSを起動する

重要なオプションは以下の通りです。
  • --engine
    現状はMySQL(とAurora)のみ対応です
  • --engine-version
    対応バージョンは現時点で最新のMySQL5.6(5.6.27)、5.7(5.7.16)、Aurora(1.10a)以降のバージョンとなります
  • --enable-iam-database-authentication
    IAM認証を有効にします
  • --master-user-password
    詳しくは後述しますがIAM認証を有効としても最初はここで決めたMasterユーザのID/パスワードでログインする必要があるため念のため複雑なものにしましょう
  • --publicly-accessible
    Public Accessを有効にします
ちなみに、既存のインスタンスを変更する場合にも modify-db-(instance|cluster) コマンドにも --enable-iam-database-authentication オプションがあり変更可能です。

IAM認証用のユーザを作成する

MySQLのユーザ作成

IAM認証を利用する場合、専用のユーザを作成する必要があります。先ほどのMasterユーザでの接続はここで必要となります。 ポイントは以下の通りです。
  • CREATE USER
    • ユーザ作成時に 'ユーザ名'@'%' で作成し、Host部を % (あらゆるIP/Hostからアクセス可能)とする
    • IDENTIFIED WITH AWSAuthenticationPlugin as 'RDS' でIAM認証プラグインでの認証とする
  • GRANT
    • REQUIRE SSL = SSLによる接続を必須とする

IAM Roleを作成

以下のようなPolicyを持つロールを作成します。 ここでのポイントは以下の通りです。
  • rds-db:connect がRDSへのIAM認証を許可するためのAction
  • Resource部の形式は arn:aws:rds-db:リージョン:アカウントID:dbuser:DBリソースID/ユーザ名
    DBリソースIDは aws rds describe-db-instances を実行すると DbiResourceId の項目で確認可能です
  • logs: でCloudWatchLogsの権限を与えているのは、Lambda Functionはログ出力先であるCloudWatchLogsの権限の権限が必須であるためで本件と直接的な関係はありません

Lambda Functionのデプロイ

いよいよ、Lambda Functionをデプロイして接続してみます。MySQLへの接続ライブラリはMySQL公式のmysql-connector-pythonを使用します。こういった単独で動くLambda Functionを依存毎まとめてデプロイするのには手前味噌ですがlamveryというツールが楽なのでこちらを使用します。ツールを使用せずにライブラリを同梱してデプロイする方法は公式ドキュメントをご確認ください。 このように、virtualenv内で mysql-connector-python をインストールします。
次に、以下のような設定を設定ファイル .lamvery.yml に書き込みます。 SSL接続する際には証明書が必要になるのでダウンロードして同梱します。 そして、Functionを書きます。 デプロイして実行します。 キタ━━━━(゚∀゚)━━━━!!

Appendix

さて、これでSSLとIAM認証を利用したセキュアな接続が可能となりましたが、何か一つ穴があると思いませんか?

そうです。Masterユーザです。

Masterユーザは固定Passwordのまま残ってしまっています。MasterユーザはRDSの基本となるユーザで、削除することができません。なので、例えばMasterユーザはVPC内部からしか接続できないようにしてしまうのが良いのではないかと思います。そうすれば、VPC内部にクライアントのEC2を置けばコマンドラインからのオペレーションはIAM認証を意識せずに今まで通り行うことができます。以下のようなmysqlコマンドラインから以下のようなコマンドを実行します。 これで、 VPCのCIDRが 10.0.0.0/16 であれば内部からしか接続できないということになります。

まとめ

実質的にServerless(API GW + Lambda)のオンライン処理ではRDSが使えない状況で、これまでは採用を見送ったり、DynamoDBで頑張って苦労してきた方も多いかと思います。今回のIAM認証によって、全てがクリアされたわけではないものの、LambdaからRDSを使う上での一番大きな問題がクリアされました。これからServerless開発がさらに盛り上がるのではないかと思います。

また、リモートのETLツールなどから接続するためにVPNを毎回設定していたような場面でも代替手段として有効です。

全世界待望の機能を是非、皆様もお役立てください。

AWS運用自動化サービス「Cloud Automator」無料トライアルはこちらから

CATEGORY :

COMMENT ON FACEBOOK