LoginSignup
38
43

More than 5 years have passed since last update.

AWS LambdaでAmazon RDS for MySQLへ接続する(Node.js 4.3 + KMSで暗号化したMySQL接続パスワードをkms.decryptで復号化してMySQLヘ接続 + バッチ実行をSNS通知する)

Last updated at Posted at 2016-04-10

はじめに

Node.js 4.3でAWS Lambdaファンクションを作成し、MySQL(Amazon RDS for MySQL)へ接続するサンプルです。

Lambdaファンクション内のMySQL接続パスワードは平文ではなく、AWS Key Management Service(KMS)により暗号化したMysQL接続パスワード文字列を記載し、Lambdaファンクション実行時にkms.decryptでパスワードを復号化してMySQLデータベースへ接続します。

また、Lambdaファンクションの実行をAWS SNSでメール通知します。

参考資料

以下のページを参考にさせて頂きました。ありがとうございました。
http://qiita.com/Keisuke69/items/cba4b501e91da95188f8

作業用マシン

Lambdaファンクションのデプロイパッケージ作成はAmazon LinuxのEC2インスタンスで行いました。
Lambdaファンクションのデプロイパッケージ作業用マシンは以下のAMIで作成しました。

・Amazon Linux AMI 2016.03.0 (HVM), SSD Volume Type - ami-f80e0596

テスト用のAmazon RDS for MySQL

まず、Amazon RDS for MySQLインスタンスを作成し、テストデータを追加します。

[ec2-user@example-server ~]$ hostname
example-server
[ec2-user@example-server ~]$ mysql -u root -p -h example-rds-mysql-server.carvmoii2uds.ap-northeast-1.rds.amazonaws.com
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 12
Server version: 5.6.27-log MySQL Community Server (GPL)

Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP ON exampledb.* TO example_user@'192.168.0.0/255.255.0.0' IDENTIFIED BY 'exam
ple_password';
Query OK, 0 rows affected (0.00 sec)

mysql> DELETE FROM exampledb.example_table;
Query OK, 6 rows affected (0.00 sec)

mysql> SELECT * FROM exampledb.example_table;
Empty set (0.00 sec)

mysql> INSERT INTO exampledb.example_table( id, insert_date) VALUES( 1, '2016-03-10 12:30:00' );
Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO exampledb.example_table( id, insert_date) VALUES( 2, '2016-03-20 12:30:00' );
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO exampledb.example_table( id, insert_date) VALUES( 3, '2016-03-30 13:40:00' );
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO exampledb.example_table( id, insert_date) VALUES( 4, '2016-04-05 14:10:00' );
Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO exampledb.example_table( id, insert_date) VALUES( 5, '2016-04-10 22:00:00' );
Query OK, 1 row affected (0.01 sec)

mysql> SELECT * FROM exampledb.example_table;
+------+---------------------+
| id   | insert_date         |
+------+---------------------+
|    1 | 2016-03-10 12:30:00 |
|    2 | 2016-03-20 12:30:00 |
|    3 | 2016-03-30 13:40:00 |
|    4 | 2016-04-05 14:10:00 |
|    5 | 2016-04-10 22:00:00 |
+------+---------------------+
5 rows in set (0.00 sec)

mysql> SELECT * FROM exampledb.example_table WHERE insert_date <= date_sub(curdate(), interval 2 week);
+------+---------------------+
| id   | insert_date         |
+------+---------------------+
|    1 | 2016-03-10 12:30:00 |
|    2 | 2016-03-20 12:30:00 |
+------+---------------------+
2 rows in set (0.00 sec)

mysql>

Lambda用のIAMロール設定

IAMロール[lambda_basic_vpc_execution]に以下のIAMポリシーを設定する。

・KMSによる暗号化したパスワード復号化用のIAMポリシー

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1448696327000",
            "Effect": "Allow",
            "Action": [
                "kms:*"
            ],
            "Resource": [
                "arn:aws:kms:ap-northeast-1:**************:key/KMSの暗号化キーのARNを指定する"
            ]
        }
    ]
}

・SNS通知用のIAMポリシー

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1460306102000",
            "Effect": "Allow",
            "Action": [
                "sns:Publish"
            ],
            "Resource": [
                "arn:aws:sns:ap-northeast-1:***********:example-lambda-mysql-sns"
            ]
        }
    ]
}

・Lambda VPC用のポリシー

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:CreateNetworkInterface",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DetachNetworkInterface",
        "ec2:DeleteNetworkInterface"
      ],
      "Resource": "*"
    }
  ]
}

作業用マシンにNode.jsとnpmインストール

作業用マシン(Amazon Linux)にNode.jsとnpmをインストールします。

[ec2-user@example-server ~]$ hostname
example-server
[ec2-user@example-server ~]$ sudo yum -y update


[ec2-user@example-server ~]$ uname -a
Linux example-server 4.4.5-15.26.amzn1.x86_64 #1 SMP Wed Mar 16 17:15:34 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
[ec2-user@example-server ~]$

[ec2-user@example-server ~]$ sudo yum install nodejs npm --enablerepo=epel

[ec2-user@example-server ~]$ node -v
v0.10.42
[ec2-user@example-server ~]$ npm -v
1.3.6
[ec2-user@example-server ~]$

AWS Lambdaファンクションのデプロイパッケージを作成する

[ec2-user@example-server ~]$ mkdir /home/ec2-user/example-lambda-mysql-select
[ec2-user@example-server ~]$
[ec2-user@example-server ~]$ cd /home/ec2-user/example-lambda-mysql-select
[ec2-user@example-server example-lambda-mysql-select]$ pwd
/home/ec2-user/example-lambda-mysql-select
[ec2-user@example-server example-lambda-mysql-select]$ npm install mysql

Lambdaファンクションのコードを[example.js]ファイルとして保存します。

[example.js]ファイルには後述の[Node.jsのAWS Lambdaファンクションサンプルコード(example.js)]の内容を記述します。

[ec2-user@example-server example-lambda-mysql-select]$ vi /home/ec2-user/example-lambda-mysql-select/example.js

Lambdaファンクションのデプロイパッケージを作成します。zipでexample-lambda-mysql-select配下を固めます。

[ec2-user@example-server example-lambda-mysql-select]$ pwd
/home/ec2-user/example-lambda-mysql-select
[ec2-user@example-server example-lambda-mysql-select]$ zip -r example-lambda-mysql-select.zip .

[ec2-user@example-server example-lambda-mysql-select]$ ls -lrta /home/ec2-user/example-lambda-mysql-select/
total 272
drwxrwxr-x  3 ec2-user ec2-user   4096 Apr 11 00:55 node_modules
-rw-rw-r--  1 ec2-user ec2-user   2649 Apr 11 00:55 example.js
drwx------ 21 ec2-user ec2-user   4096 Apr 11 00:55 ..
-rw-rw-r--  1 ec2-user ec2-user 258434 Apr 11 00:56 example-lambda-mysql-select.zip
drwxrwxr-x  3 ec2-user ec2-user   4096 Apr 11 00:56 .
[ec2-user@example-server example-lambda-mysql-select]$

zipファイル化したデプロイパッケージをS3へアップロードします。このzipファイルは後でLambdaファンクション作成時に利用します。

[ec2-user@example-server example-lambda-mysql-select]$ aws s3 cp /home/ec2-user/example-lambda-mysql-select/example-lambda-mysql-select.zip s3://適当なS3バケット/
upload: ./example-lambda-mysql-select.zip to s3://適当なS3バケット/example-lambda-mysql-select.zip
[ec2-user@example-server example-lambda-mysql-select]$

Node.jsのAWS Lambdaファンクションサンプルコード(example.js)

以下がLambdaファンクションのコードです。

example.js
var AWS = require('aws-sdk');
var mysql = require('mysql');

// KMSにより暗号化したMySQL接続用のパスワード文字列
var kmsEncyptedToken = "CiC*********(KMSで暗号化したMySQL接続用のパスワード文字列)***********UI=";

// 接続先のMySQLサーバ情報
var mysql_host = "example-rds-mysql-server.carvmoii2uds.ap-northeast-1.rds.amazonaws.com";
var mysql_user = "example_user";
var mysql_dbname = "exampledb";
var mysql_password = "";

var sns_topic_arn = "arn:aws:sns:ap-northeast-1:*******************:example-lambda-mysql-sns";

exports.handler = function(event, context){

    // 実行するSQL文
    var sql = "SELECT * FROM exampledb.example_table WHERE insert_date <= date_sub(curdate(), interval 2 week)";

    if (kmsEncyptedToken && kmsEncyptedToken !== "<kmsEncryptedToken>") {

        var encryptedBuf = new Buffer(kmsEncyptedToken, 'base64');
        var cipherText = {CiphertextBlob: encryptedBuf};

        var kms = new AWS.KMS({ region: 'ap-northeast-1' });
        var sns = new AWS.SNS({ region: 'ap-northeast-1' });

        // KMSにより暗号化したパスワード文字列の復号化
        kms.decrypt(cipherText, function (err, data) {
            if (err) {

                console.log("CipherText Decrypt error: " + err);
                context.fail(err);

            } else {

                mysql_password = data.Plaintext.toString('ascii');

                var connection = mysql.createConnection({
                    host     : mysql_host,
                    user     : mysql_user,
                    password : mysql_password,
                    database : mysql_dbname
                });

                console.log("MySQL Server Name: " + mysql_host);
                console.log("MySQL User Name: " + mysql_user);
                console.log("MySQL Database Name: " + mysql_dbname);
                console.log("MySQL Exec SQL: " + sql);

                // MySQLデータベースへの接続
                connection.connect();

                // MySQLデータベースでSQL実行
                connection.query(sql, function(err, rows, fields) {
                    if (err) {

                        console.log("MySQL Select Error");
                        context.fail(err);
                        sns.publish({
                            Message: 'Lambda Function Error',
                            Subject: 'Lambda Function Error',
                            TopicArn: sns_topic_arn
                        }, function(err, data){
                            if(err) throw err;
                            else context.fail('SNS Publish Error');
                        });

                        throw err;

                    } else {

                    console.log("MySQL Select Success");
                    console.log(rows);
                    console.log(fields);
                    }
                });

                connection.end(function(err) {

                    sns.publish({
                        Message: 'Lambda Function Success',
                        Subject: 'Lambda Function Success',
                        TopicArn: sns_topic_arn
                    }, function(err, data){
                        if(err) throw err;
                        else context.fail('SNS Publish Error');
                    });

                    context.done();
                });
            }
        });

    } else {

        context.fail("kmsEncyptedToken has not been set.");

    }

    console.log('end');

};

Lambdaファンクション作成

先ほど作成したLambdaのデプロイパッケージ[example-lambda-mysql-select.zip]をLambdaにアップロードして、Lambdaファンクションを作成します。
前述の手順でS3にパッケージアップロードしていると思いますので、それを選択します。
 s3://適当なS3バケット/example-lambda-mysql-select.zip

LambdaファンクションのIAMポリシーは[lambda_basic_vpc_execution]を選択、セキュリティグループは、Amazon RDS for MySQLインスタンスへMySQL接続可能な設定のセキュリティグループを選択して、Lambdaファンクションを作成します。

Lambdaファンクション実行

Lambdaファンクションを実行し、正常にMySQLデータベースへ接続出来れば、Lambdaの[Log outPut]に以下のようにMySQLデータベースのSELECT結果が表示されます。
また、ANS SNSで指定したメールアドレス宛に[Lambda Fanction Success]メールが届きます。

START RequestId: 442b53cf-ff43-11e5-9dc8-2123a6cf68bb Version: $LATEST
2016-04-10T17:40:08.926Z    442b53cf-ff43-11e5-9dc8-2123a6cf68bb    end
2016-04-10T17:40:10.127Z    442b53cf-ff43-11e5-9dc8-2123a6cf68bb    MySQL Server Name: example-rds-mysql-server.carvmoii2uds.ap-northeast-1.rds.amazonaws.com
2016-04-10T17:40:10.183Z    442b53cf-ff43-11e5-9dc8-2123a6cf68bb    MySQL User Name: example_user
2016-04-10T17:40:10.183Z    442b53cf-ff43-11e5-9dc8-2123a6cf68bb    MySQL Database Name: exampledb
2016-04-10T17:40:10.183Z    442b53cf-ff43-11e5-9dc8-2123a6cf68bb    MySQL Exec SQL: SELECT * FROM exampledb.example_table WHERE insert_date <= date_sub(curdate(), interval 2 week)
2016-04-10T17:40:10.346Z    442b53cf-ff43-11e5-9dc8-2123a6cf68bb    MySQL Select Success
2016-04-10T17:40:10.346Z    442b53cf-ff43-11e5-9dc8-2123a6cf68bb    [ RowDataPacket { id: 1, insert_date: Thu Mar 10 2016 12:30:00 GMT+0000 (UTC) },
  RowDataPacket { id: 2, insert_date: Sun Mar 20 2016 12:30:00 GMT+0000 (UTC) } ]
2016-04-10T17:40:10.567Z    442b53cf-ff43-11e5-9dc8-2123a6cf68bb    [ FieldPacket {
    catalog: 'def',
    db: 'exampledb',
    table: 'example_table',
    orgTable: 'example_table',
    name: 'id',
    orgName: 'id',
    charsetNr: 63,
    length: 12,
    type: 3,
    flags: 0,
    decimals: 0,
    default: undefined,
    zeroFill: false,
    protocol41: true },
  FieldPacket {
    catalog: 'def',
    db: 'exampledb',
    table: 'example_table',
    orgTable: 'example_table',
    name: 'insert_date',
    orgName: 'insert_date',
    charsetNr: 63,
    length: 19,
    type: 12,
    flags: 128,
    decimals: 0,
    default: undefined,
    zeroFill: false,
    protocol41: true } ]
END RequestId: 442b53cf-ff43-11e5-9dc8-2123a6cf68bb
REPORT RequestId: 442b53cf-ff43-11e5-9dc8-2123a6cf68bb  Duration: 2670.49 ms    Billed Duration: 2700 ms    Memory Size: 128 MB Max Memory Used: 30 MB  

以上になります。

38
43
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
43