search
LoginSignup
4

More than 5 years have passed since last update.

posted at

updated at

Organization

AWS LambdaでKMSを使ってセキュアにRDSへ接続する(インフラ編)

この記事はWHITEPLUS Advent Calendar 2016 10日目になります。

こんにちは。株式会社ホワイトプラス、エンジニアの @hachihiro224です。
一応マネージャーをさせてもらっているので、フロントエンド、バックエンド、インフラといろいろな領域を見ています。

今日は弊社で利用しているAWS Lambdaの環境構築について紹介します。

RDSに接続するAWS Lambdaの環境構築

利用するサービスは次の3つです。

  • AWS Lambda(処理本体)
  • AWS RDS(データベース)
  • AWS KMS(接続情報の暗号化)

次の順で構築していきます。

  1. Lambdaを配置するVPC Subnetの作成
  2. KMSを利用した接続情報の暗号化
  3. RDSに接続するLambda Functionの生成

Lambdaを配置するVPC Subnetの作成

VPCにLamba Functionを配置する際に気をつけるポイントです。

  • KMSを利用するためにはインターネットにアクセス出来る必要がある
  • パブリックサブネットに配置してもインターネットにアクセスできない

ということで、プライベートサブネットにNATゲートウェイを配置して、インターネットアクセス可能にします。

こちらの記事を参考に構築しました。
http://yoshidashingo.hatenablog.com/entry/2015/12/18/041217

サブネットは異なるアベイラビリティゾーンのものを一つずつ用意します。

KMSを利用した接続情報の暗号化

RDSに接続する際、考慮しなければならないのが接続情報の管理です。
KMSを利用すると接続情報を暗号化し、安心してコードに載せることができます。

KMSの利用はこちらの記事を参考にしました。
http://dev.classmethod.jp/cloud/decrypt-sensitive-data-with-kms-on-lambda-invocation/

環境を構築したら次のコマンドで接続情報を暗号化します。

# db_host
aws kms encrypt --key-id $KEYID --plaintext 'db_host'
# db_user
aws kms encrypt --key-id $KEYID --plaintext 'db_user'
# db_password
aws kms encrypt --key-id $KEYID --plaintext 'db_password'

RDSに接続するLambda Functionの生成

いよいよLambdaでRDSに接続します。

次のような作業ディレクトリを作ります。

sample_function
├── create-function.sh
└── src
    └── sample_function.py

必要なパッケージをインストールします。

pip install boto3 PyMySQL

RDSに接続するlambda functionを作成します。

sample_function.py
# -*- coding: utf-8 -*-

import sys
import logging
import pymysql
import base64
import boto3

logger = logging.getLogger()
logger.setLevel(logging.INFO)
kms = boto3.client('kms')


def decrypt(encrypted):
    return kms.decrypt(CiphertextBlob=base64.b64decode(encrypted))['Plaintext']

# rds settings
rds_host = decrypt('暗号化されたdb_host')
name = decrypt('暗号化されたdb_user')
password = decrypt('暗号化されたdb_password')
db_name = 'データベース名'

try:
    conn = pymysql.connect(rds_host, user=name, passwd=password, db=db_name, connect_timeout=5, charset='utf8')
except:
    logger.error("ERROR: Unexpected error: Could not connect to MySql instance.")
    sys.exit()


def lambda_handler(event, context):
    with conn.cursor() as cur:
        num = cur.execute('SELECT NOW()')
        for row in cur:
            logger.info('接続確認 %s' % row[0])

        return "Got %d items from RDS MySQL table" % num

Lambda Functionの実行に必要なIAM ROLEを作成します。
VPC権限: https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/vpc.html
KMS権限: インラインポリシーで以下を追加

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1448696327000",
            "Effect": "Allow",
            "Action": [
                "kms:Decrypt"
            ],
            "Resource": [
                "arn:aws:kms:ap-northeast-1:123456789012:key/xxx-yyy-zzz"
            ]
        }
    ]
}

lambda functionをAWSに登録します。
sample-function直下で以下のシェルを実行します。

create-function.sh
#!/bin/bash

# TODO: update function_name
FUNCTION_NAME=sample_function

cd "$(dirname "$0")"/src
mkdir bundle
cp *.py bundle/
cd bundle

pip freeze > require.txt
[ -s require.txt ] && pip install -r require.txt -t .

zip -r upload.zip .

aws lambda create-function \
    --function-name ${FUNCTION_NAME} \
    --runtime python2.7 \
    --role #lambdaとKMSが使えるIAM ROLEのarn \
    --handler ${FUNCTION_NAME}.lambda_handler \
    --vpc-config SubnetIds=#1.で作成したvpc subnet,SecurityGroupIds=#RDSに接続可能なsecurity group \
    --zip-file fileb://upload.zip \
    --timeout 60

cd .. && rm -rf bundle

以下のコマンドでLambda Functionを実行します。

aws lambda invoke --function-name sample_function --payload '' response && cat response

無事にRDSへ接続するLambda Functionの作成ができました。

まとめ

LambdaでのVPC接続はVPCの設定、KMSでの接続情報の暗号化など関連するタスクがあり、始めるまでのハードルが高めです。
今回紹介した内容が少しでも参考になればと思います。

明日は弊社デザイナー @hayaoo の「再考、デザイン・スプリントの誤解と3つのメリット」です。

ホワイトプラスではエンジニアを募集しています

ホワイトプラスでは、新しい技術にどんどん挑戦したい!という技術で事業に貢献したいエンジニアを募集しております。

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
What you can do with signing up
4