RDSとLambdaを安全に接続!SecretsManager設定手順

はじめに

AWSを用いたモダンなアプリケーション開発において、VPC内のAWS LambdaからAmazon RDS(データベース)へ安全に接続するアーキテクチャの構築は、インフラエンジニアにとって必須のスキルです。しかし、データベースの接続パスワードをLambdaの環境変数に直接ハードコードすることは、セキュリティ上の重大なリスクとなります。

本記事では、AWSのベストプラクティスである「AWS Secrets Manager」を活用し、データベースの認証情報を安全に管理・自動ローテーション(更新)しながら、VPC内のLambdaからRDS(MySQL)へセキュアに接続するための具体的な設定手順をステップバイステップで解説します。

前提知識/必要な理由

データベース認証情報の管理における課題

多くの開発現場では、初期構築時にデータベースのユーザー名やパスワードをアプリケーションの設定ファイルやLambdaの環境変数に格納してしまいがちです。これには以下のリスクが伴います。

  • ソースコードの共有やIAM権限の設定ミスによる、パスワードの漏洩リスク。
  • パスワード変更の運用コストが高く、長期間同じパスワードが使い回される。
  • パスワード変更時にシステムの一時的なダウンタイムが発生する。

AWS Secrets Managerが提供する解決策

AWS Secrets Managerを導入することで、認証情報を安全に暗号化(AWS KMSを使用)して保存できます。さらに、標準で用意されているローテーション機能を利用すれば、アプリケーションのダウンタイムなしにデータベースのパスワードを定期的に自動更新することが可能になります。

具体的な設定手順・設計方法

ステップ1:RDSインスタンスとVPCの準備

まずは前提として、ターゲットとなるRDSインスタンスが特定のVPC(プライベートサブネット)内に配置されていることを確認します。Lambda関数も同様のVPCに配置し、双方のセキュリティグループ間で通信を許可(通常はポート3306)しておきます。

ステップ2:Secrets Managerでシークレットを登録する

データベースの認証情報をSecrets Managerに登録します。

  • AWS管理コンソールで「Secrets Manager」を開き、「新しいシークレットを保存する」をクリックします。
  • シークレットのタイプとして「Amazon RDS データベースの資格情報」を選択します。
  • RDSの「ユーザー名」と「パスワード」を入力し、対象の「RDSデータベース」を選択して「次へ」進みます。
  • シークレット名(例: prod/rds/mysql)を入力し、作成を完了します。

ステップ3:ローテーションの設定

作成したシークレットの詳細画面から、自動ローテーションを設定します。

  • 「自動ローテーション」タブから「ローテーションを編集」を選択します。
  • ローテーションの間隔(例: 30日)を設定します。
  • ローテーションを実行するLambda関数として「新しいLambda関数を作成」を選択し、自動生成されるローテーション用テンプレートを利用します。これにより、Secrets ManagerがRDS側のパスワードと自身の保管するシークレットを自動的に同期・更新してくれます。

ステップ4:Lambda関数からSecrets Manager経由でRDSに接続するコードの実装

実際にVPC内のLambdaからSecrets ManagerのAPIを呼び出し、取得した認証情報を使ってRDSに接続するPythonコードの例です。ここではMySQLへの接続に外部パッケージの PyMySQL を使用します。

【補足】外部パッケージ(PyMySQL)の導入手順

Lambda環境には標準で PyMySQL が含まれていないため、事前にLambdaレイヤーとして登録するか、コードと一緒にZIPアーカイブしてデプロイする必要があります。ローカル環境で以下のコマンドを実行し、パッケージを準備してください。

# 作業用ディレクトリの作成
mkdir -p python
cd python

# パッケージを特定のディレクトリにインストール
pip install PyMySQL -t .

# ZIP圧縮してLambdaレイヤーにアップロード可能な形式にする
cd ..
zip -r pymysql_layer.zip python/

作成した pymysql_layer.zip をAWS Lambdaの「レイヤー」として登録し、対象のLambda関数に紐付けます。

接続用Lambda関数のサンプルコード

import json
import boto3
import pymysql
from botocore.exceptions import ClientError

def get_secret():
    secret_name = "prod/rds/mysql"
    region_name = "ap-northeast-1"

    # Secrets Managerクライアントの作成
    session = boto3.session.Session()
    client = session.client(
        service_name='secretsmanager',
        region_name=region_name
    )

    try:
        get_secret_value_response = client.get_secret_value(
            SecretId=secret_name
        )
    except ClientError as e:
        raise e

    # デコードされたシークレット文字列をJSONとしてロード
    secret = get_secret_value_response['SecretString']
    return json.loads(secret)

def lambda_handler(event, context):
    # Secrets Managerから接続情報を取得
    db_credentials = get_secret()
    
    # 接続情報の設定
    db_host = db_credentials['host']
    db_user = db_credentials['username']
    db_password = db_credentials['password']
    db_name = db_credentials['dbname']
    
    # データベースへの接続試行
    try:
        connection = pymysql.connect(
            host=db_host,
            user=db_user,
            password=db_password,
            database=db_name,
            connect_timeout=5
        )
        print("SUCCESS: Connection to RDS instance succeeded")
        
        with connection.cursor() as cursor:
            cursor.execute("SELECT VERSION()")
            result = cursor.fetchone()
            print(f"Database version: {result[0]}")
            
        connection.close()
        return {
            'statusCode': 200,
            'body': json.dumps('Database connection successful!')
        }
    except Exception as e:
        print(f"ERROR: Unexpected error: {e}")
        raise e

実務での注意点

1. VPCエンドポイントの設定を忘れないこと

VPC内のLambdaから外部のAWS API(Secrets Managerなど)を呼び出す場合、インターネットへの出口(NAT Gateway)がないプライベートサブネットでは通信が遮断されます。これを防ぐため、Secrets Manager用のインターフェイス型VPCエンドポイント(AWS PrivateLink)を必ずVPC内に設置してください。

2. IAMロールの権限は最小限にする

Lambda関数に付与するIAMロールには、必要なシークレットのみを取得できるよう限定的な権限を設定してください。以下のようなIAMポリシーをアタッチします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "secretsmanager:GetSecretValue",
            "Resource": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:prod/rds/mysql-*"
        }
    ]
}

3. 接続のキャッシュを考慮する

Lambdaが実行されるたびにSecrets Managerを呼び出すと、APIの呼び出し課金(APIコール回数)が増加するほか、応答性能が低下します。Lambda関数のハンドラー外(グローバルスコープ)でシークレット情報を一時的にキャッシュするか、Secrets Manager用の拡張ライブラリを使用してキャッシュ制御を行うのが実務での設計ベストプラクティスです。

まとめ

本記事では、AWS LambdaからVPC内のRDSへ安全に接続するための、AWS Secrets Managerを用いた設定手順とベストプラクティスを解説しました。

この構成を導入することで、認証情報のハードコードを完全に排除し、安全な自動パスワードローテーションを実現できます。インフラ設計の基本でありながら非常に強固なセキュリティ効果を発揮するため、実務での構築・運用の際にはぜひ本手順を参考にしてください。

上部へスクロール