RDS Proxyの2つの認証方式
以下のページで、RDS Proxyを通じてLambdaからRDSにアクセスする方法をご紹介しました。
上記ページでは、LambdaからRDS Proxyに接続する際に、パスワード認証を選択しました。
改めて、以下にRDS Proxyを使用する場合の認証フローを整理します。
クライアントがRDS Proxyに接続する上で、2つの方式が選択可能です。
1つ目はパスワード認証です。一般的なRDBに接続時と同様の方式です。
2つ目はIAM認証です。IAMポリシーベースで接続可能なDBユーザーを指定する方式です。
なおどちらの認証方式を選択したとしても、RDS Proxy〜RDS間の接続はSecret Managerのシークレット(ユーザー名・パスワード)を使用する認証となります。
今回は2つの認証方式を比較する形で確認します。
構築する環境
基本的な構成は先述のページと同様です。
異なる点は2つの認証用として、2つのRDS Proxyを作成している点と、それらを使用するLambda関数を用意している点です。
今回作成する構成の挙動を簡単に説明します。
DBインスタンスに日時データを保存するテーブルを作成します。
API GatewayにHTTPリクエストが届くと、バックエンドのLambdaが呼び出され、現在日時を取得します。
取得した日時データをRDS Proxy経由でDBインスタンスに保存します。
加えてDBインスタンスに保存されている全データを取得し、HTTPレスポンスとして返します。
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。
https://github.com/awstut-an-r/awstut-fa/tree/main/039
テンプレートファイルのポイント解説
基本的な構成は以下のページと同様です。
本ページでは、RDS Proxyや、RDS Proxyを通じたDBインスタンスへの接続に関する内容を中心として取り上げます。
RDS Proxyの2つの認証方式
パスワード認証のRDS Proxy
まずパスワード認証から確認します。
Resources:
DBProxy1:
Type: AWS::RDS::DBProxy
Properties:
Auth:
- IAMAuth: DISABLED
AuthScheme: SECRETS
SecretArn: !Ref Secret
DBProxyName: !Sub "${Prefix}-DBProxy-01"
EngineFamily: !Ref DBProxyEngineFamily
IdleClientTimeout: !Ref IdleClientTimeout
RequireTLS: false
RoleArn: !GetAtt DBProxyRole.Arn
VpcSecurityGroupIds:
- !Ref DBProxySecurityGroup
VpcSubnetIds:
- !Ref DBSubnet1
- !Ref DBSubnet2
Code language: YAML (yaml)
ポイントは2つです。
1つ目はAuthプロパティです。
こちらはIAM認証は使用しないため、IAMAuthプロパティを「DISABLED」と設定します。
2つ目はRequireTLSプロパティです。
パスワード認証の場合、TLSを有効化する必要がないため「false」とします。
IAM認証のRDS Proxy
次にIAM認証を確認します。
Resources:
DBProxy2:
Type: AWS::RDS::DBProxy
Properties:
Auth:
- IAMAuth: REQUIRED
AuthScheme: SECRETS
SecretArn: !Ref Secret
DBProxyName: !Sub "${Prefix}-DBProxy-02"
EngineFamily: !Ref DBProxyEngineFamily
IdleClientTimeout: !Ref IdleClientTimeout
RequireTLS: true
RoleArn: !GetAtt DBProxyRole.Arn
VpcSecurityGroupIds:
- !Ref DBProxySecurityGroup
VpcSubnetIds:
- !Ref DBSubnet1
- !Ref DBSubnet2
Code language: YAML (yaml)
先ほどの2つのポイントを確認します。
1点目のAuthプロパティ内のIAMAuthプロパティは「REQUIRED」に設定します。
これでIAM認証が有効化されます。
2点目のRequireTLSプロパティは「true」とします。
これはIAM認証の必須要件です。
IAM 認証を使用してプロキシに接続する場合は、Transport Layer Security (TLS) / Secure Sockets Layer (SSL) を使用していることを確認してください。
IAM 認証を使用したプロキシへの接続
RDS Proxyに接続するLambda関数
Lambdaレイヤー
2つのLambda関数では、それぞれmysql-connector-pythonを使用して、MySQLタイプのDBインスタンスに接続します。
今回は同パッケージをLambdaレイヤーとして用意し、2関数から参照する形とします。
Resources:
LambdaLayer:
Type: AWS::Lambda::LayerVersion
Properties:
CompatibleRuntimes:
- !Ref Runtime
Content:
S3Bucket: !Ref CodeS3Bucket
S3Key: !Ref CodeS3Key3
Description: !Ref Prefix
LayerName: !Ref Prefix
Code language: YAML (yaml)
以下のコマンドはLambdaレイヤー用のZIPファイルを作成し、S3バケットにアップロードする例です。
$ pip3 install mysql-connector-python -t ./python
$ zip -r layer.zip python
$ aws s3 cp layer.zip s3://[s3-bucket-name]/
Code language: Bash (bash)
Lambdaレイヤーの詳細につきましては、以下のページをご確認ください。
パスワード認証のLambda関数
パスワード認証で接続するLambda関数を確認します。
Resources:
Function1:
Type: AWS::Lambda::Function
Properties:
Environment:
Variables:
DB_ENDPOINT_PORT: !Ref MySQLPort
DB_NAME: !Ref DBName
DB_PASSWORD: !Ref DBMasterUserPassword
DB_PROXY_ENDPOINT_ADDRESS: !Ref DBProxyEndpointAddress1
DB_TABLENAME: !Ref DBTableName
DB_USER: !Ref DBMasterUsername
REGION: !Ref AWS::Region
Code:
S3Bucket: !Ref CodeS3Bucket
S3Key: !Ref CodeS3Key1
FunctionName: !Sub "${Prefix}-function-01"
Handler: !Ref Handler
Layers:
- !Ref LambdaLayer
Runtime: !Ref Runtime
Role: !GetAtt FunctionRole1.Arn
Timeout: !Ref Timeout
VpcConfig:
SecurityGroupIds:
- !Ref FunctionSecurityGroup
SubnetIds:
- !Ref FunctionSubnet
Code language: YAML (yaml)
ポイントは環境変数です。
Environmentプロパティで関数内で使用可能な環境変数を定義することができます。
特に重要な変数はDBインスタンスに接続するためのパスワード(DB_PASSWORD)です。
こちらの関数はパスワード認証を行うためです。
こちらの関数用のIAMロールは以下の通りです。
Resources:
FunctionRole1:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- lambda.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
Code language: YAML (yaml)
特別な設定は不要です。
Lambda用として用意されているAWS管理ポリシーのみを指定しています。
関数の中身を確認します。
特にMySQL接続用オブジェクト作成の箇所を取り上げます。
import mysql.connector
import os
db_endpoint_port = os.environ['DB_ENDPOINT_PORT']
db_name = os.environ['DB_NAME']
db_password = os.environ['DB_PASSWORD']
db_proxy_endpoint_address = os.environ['DB_PROXY_ENDPOINT_ADDRESS']
db_tablename = os.environ['DB_TABLENAME']
db_user = os.environ['DB_USER']
region = os.environ['REGION']
def lambda_handler(event, context):
conn = mysql.connector.connect(
host=db_proxy_endpoint_address,
port=db_endpoint_port,
user=db_user,
password=db_password,
database=db_name
)
Code language: Python (python)
Lambdaレイヤーに用意されたmysql-connector-python(mysql.connector)を使用します。
こちらはパスワード認証ですので、接続先のRDS Proxyエンドポイント等に加えて、パスワードを指定します。
IAM認証のLambda関数
IAM認証で接続するLambda関数を確認します。
Resources:
Function2:
Type: AWS::Lambda::Function
Properties:
Environment:
Variables:
DB_ENDPOINT_PORT: !Ref MySQLPort
DB_NAME: !Ref DBName
#DB_PASSWORD: !Ref DBMasterUserPassword
DB_PROXY_ENDPOINT_ADDRESS: !Ref DBProxyEndpointAddress2
DB_TABLENAME: !Ref DBTableName
DB_USER: !Ref DBMasterUsername
REGION: !Ref AWS::Region
SSLCERTIFICATE: AmazonRootCA1.pem
Code:
S3Bucket: !Ref CodeS3Bucket
S3Key: !Ref CodeS3Key2
FunctionName: !Sub "${Prefix}-function-02"
Handler: !Ref Handler
Layers:
- !Ref LambdaLayer
Runtime: !Ref Runtime
Role: !GetAtt FunctionRole2.Arn
Timeout: !Ref Timeout
VpcConfig:
SecurityGroupIds:
- !Ref FunctionSecurityGroup
SubnetIds:
- !Ref FunctionSubnet
Code language: YAML (yaml)
先ほどのパスワード認証を行う関数と異なる点は、定義する環境変数です。
こちらはIAM認証ですから、パスワードは不要ですので、コメントアウトしています。
後述しますが、Lambda関数用のデプロイパッケージに同梱する証明書のファイル名も、変数として定義します。
こちらの関数用のIAMロールは以下の通りです。
Resources:
FunctionRole2:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- lambda.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
Policies:
- PolicyName: AllowRDSDBConnectPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- rds-db:connect
Resource:
- !Sub "arn:aws:rds-db:${AWS::Region}:${AWS::AccountId}:dbuser:${DBProxyId2}/${DBMasterUsername}"
Code language: YAML (yaml)
先ほど取り上げたAWS管理ポリシーに加えて、インラインポリシーを定義します。
DBインスタンス作成時に指定したDBユーザーに対して、「rds-db:connect」アクションを許可する内容です。
こちらのポリシーは以下のページを参照して定義しました。
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.IAMPolicy.html
関数の中身を確認します。
特にMySQL接続用オブジェクト作成の箇所を取り上げます。
import mysql.connector
import os
db_endpoint_port = os.environ['DB_ENDPOINT_PORT']
db_name = os.environ['DB_NAME']
#db_password = os.environ['DB_PASSWORD']
db_proxy_endpoint_address = os.environ['DB_PROXY_ENDPOINT_ADDRESS']
db_tablename = os.environ['DB_TABLENAME']
db_user = os.environ['DB_USER']
region = os.environ['REGION']
ssl_certificate = os.environ['SSLCERTIFICATE']
os.environ['LIBMYSQL_ENABLE_CLEARTEXT_PLUGIN'] = '1'
client = boto3.client('rds', region_name=region)
def lambda_handler(event, context):
token = client.generate_db_auth_token(
DBHostname=db_proxy_endpoint_address,
Port=db_endpoint_port,
DBUsername=db_user,
Region=region)
conn = mysql.connector.connect(
host=db_proxy_endpoint_address,
user=db_user,
password=token,
port=db_endpoint_port,
database=db_name,
ssl_ca=ssl_certificate)
Code language: Python (python)
以下のページを参考に、IAM認証時のオブジェクト作成を実装します。
ポイントは2点あります。
1点目は直接パスワードを設定するのではなく、一時的なトークンを作成し、それをパスワードとして使用するという点です。
client.generate_db_auth_tokenでトークンを作成します。
2点目は証明書の指定です。
IAM認証でDBインスタンス接続する際に、証明書を指定する必要があります。
この証明書はAWSからダウンロードすることができます。
使用できる .pem ファイルとしては、Amazon Trust Services から「Amazon root CA 1 trust store」をダウンロードします。
RDS Proxy の概念と用語
以下のコマンドは証明書をダウンロードする例です。
$ curl -OL https://www.amazontrust.com/repository/AmazonRootCA1.pem
Code language: Bash (bash)
Lambda関数用のパッケージ作成時に、この証明書(AmazonRootCA1.pem)も含める形でZIP化してデプロイします。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- API Gatewayのエンドポイント:https://6ug0w6jg5b.execute-api.ap-northeast-1.amazonaws.com
AWS Management Consoleからも、リソースの作成状況を確認します。
RDS Proxyの認証に関する項目を確認します。
それぞれパスワード認証、IAM認証として設定したRDS Proxyです。
動作確認
準備が整いましたので、API Gatewayにアクセスします。
まずパスワード認証で接続するLambda関数の動作を確認します。
/func1が本関数のURLです。
正常にレスポンスが返ってきました。
パスワード認証で接続できることを確認しました。
続いてIAM認証で接続するLambda関数の動作を確認します。
/func2が本関数のURLです。
正常にレスポンスが返ってきました。
IAM認証で接続できることを確認しました。
まとめ
LambdaからRDS Proxy経由でDBインスタンスに接続する際の、2つの認証方式を確認しました。