こんにちは、中の人です。
今回は、 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)に接続してみた」で詳しく解説されておりますので、後者に対する対策を考えてみました。
○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で地味な何かつくってみますー」です。
お楽しみに!