LambdaからセキュアにRDSに接続する

こんにちは、中の人です。

今回は、 AWS Lambda Advent Calendar 2014 に投稿した内容を紹介します。

昨日のAWS Lambda Advent Calendar 2014は玉川さんの非常に心温まる「 ばあちゃんにひ孫の写真をAPIで印刷&郵送する 」という記事でした。

今日は11日目としてLambdaからセキュアにRDSに接続してみたいと思います。

○Lambda⇒RDSへ接続するときの課題点

・node.jsにMySQLパッケージを追加が必要

・LambdaがIPアドレス固定でないので、RDSのセキュリティグループをフル開放する必要がある

といった点がポイントとなります。

前者は「AWS LambdaでRDS(MySQL)に接続してみた」で詳しく解説されておりますので、後者に対する対策を考えてみました。

cap00

○LambdaのIPアドレス用プログラムの設置

Webサーバに以下のPHPを設置します。

Lambdaから以下のプログラムにアクセスすることでLambdaのグローバルIPを取得します。

設置が難しい場合はIPアドレスを取得できるようなサービスにアクセスして取得しましょう。

1<?php2echo $_SERVER['REMOTE_ADDR’];

○Lambdaのプログラム

Lambdaでは

1. 先ほどのプログラムからLambdaのIPアドレスを取得

2. RDSにLambdaのIPアドレスを追加

3. Lambdaから安全にアクセス

という流れになります。

まず、「 AWS LambdaでRDS(MySQL)に接続してみた 」を参考にMySQLのパッケージを追加します。

サーバ上で以下を実施します。

1$ npm install mysql

以下のプログラムをindex.jsとして保存します。

1console.log('Loading event');2var aws = require('aws-sdk');3var ec2 = new aws.EC2({apiVersion: '2014-10-01'});4var mysql = require('mysql');56exports.handler = function(event, context) {7  var exec = require('child_process').exec;89  var cmd = "curl http://**************/ip.php;" // 先ほど作成したphpのプログラムでLambdaのIPを取得10  var child = exec(cmd, function(error, stdout, stderr) {11    if (!error) {12      // IP ⇒ CIDR13      var ipAddress = stdout.replace(/[\n\r]/g,"") + "/32";1415      var params = {16        GroupId: 'sg-********',17        IpPermissions: [18          {19            FromPort: 3306,20            ToPort: 3306,21            IpProtocol: 'tcp',22            IpRanges: [23              {24                CidrIp: ipAddress25              },26            ]27          },28        ],29      };30      ec2.authorizeSecurityGroupIngress(params, function(err, data) {31        if (err){32          // 既にセキュリティグループが存在すれば、そのまま実行33          result = err.toString().search( /already exists|has already been authorized/ );34          if ( result > 0 ) {35            // LambdaからRDSを参照する36            var connection = mysql.createConnection({37              host     : 'test.**********.us-east-1.rds.amazonaws.com', //RDSのエンドポイント38              user     : 'user', //MySQLのユーザ名39              password : 'password', //MySQLのパスワード40              database : 'lambda'41            });42            connection.connect();43            connection.query('select * from lambda', function(err, rows, fields) {44              if (err) throw err;45              console.log(rows);46            });47            connection.end(function(err) {48//              console.log(err);49            });50          } else {51            console.log(err.stack); 52          }53          context.done();54        }55        else{56          // LambdaからRDSを参照する57          var connection = mysql.createConnection({58            host     : 'test.**********.us-east-1.rds.amazonaws.com', //RDSのエンドポイント59            user     : 'user', //MySQLのユーザ名60            password : 'password', //MySQLのパスワード61            database : 'lambda'62          });63          connection.connect();64          connection.query('select * from lambda', function(err, rows, fields) {65            if (err) throw err;6667            console.log(rows);68          });69          connection.end(function(err) {70//            console.log(err);71          });72        }     73      });74    } else {75        console.log("error code: " + error.code + ", err: " + error);76        context.done(error,'lambda');77    }78  });79};

続いてindex.jsと先ほどインストールしたMySQLのパッケージのあるnode_modulesをまとめてzip化します。

圧縮ファイルの中身が

1index.js2/node_modules3 /mysql

の様になっていればOKです。

lambda上にzip形式でアップして実行してみましょう。

1START RequestId: a8a42c4f-8147-11e4-b54d-595292db8ce522014-12-09T15:09:10.049Z	a8a42c4f-8147-11e4-b54d-595292db8ce5	Loading event32014-12-11T15:09:10.369Z	a8a42c4f-8147-11e4-b54d-595292db8ce5	{}42014-12-11T15:09:10.249Z	a8a42c4f-8147-11e4-b54d-595292db8ce5	[ { id: 1, test: 'name' } ]5END RequestId: a8a42c4f-8147-11e4-b54d-595292db8ce567REPORT RequestId: a8a42c4f-8147-11e4-b54d-595292db8ce5	Duration: 2050.61 ms	Billed Duration: 2100 ms 	Memory Size: 128 MB	Max Memory Used: 24 MB

上記のように接続でき、SecurityGroupを確認するとLambdaのIPアドレスが追加されていればOKです。

ちょっと強引な方法ですが、工夫することでLambdaからRDSにもセキュアにアクセスすることが出来ます。

※プログラム内でsleepをかけてからMySQL接続したかった(これが出来ないので若干動作が不安定です…)のとSQL実行後にSecurityGroupを削除を実装したかったのですが、setTimeoutでうまく制御出来なかったので後日挑戦したいと思います。

もし、良い方法を御存知でしたらアドバイスください!

明日は @shot6 さんの「Lambdaで地味な何かつくってみますー」です。

お楽しみに!

この記事を書いた人

aws-recipe-user