AWS

RDS Proxyの2つの認証方式 – パスワード/IAM

スポンサーリンク
RDS Proxyの2つの認証方式 - パスワード/IAM AWS
スポンサーリンク
スポンサーリンク

RDS Proxyの2つの認証方式

以下のページで、RDS Proxyを通じてLambdaからRDSにアクセスする方法をご紹介しました。

上記ページでは、LambdaからRDS Proxyに接続する際に、パスワード認証を選択しました。

改めて、以下にRDS Proxyを使用する場合の認証フローを整理します。

Diagram of the flow of RDS Proxy Authentication.

クライアントがRDS Proxyに接続する上で、2つの方式が選択可能です。
1つ目はパスワード認証です。一般的なRDBに接続時と同様の方式です。
2つ目はIAM認証です。IAMポリシーベースで接続可能なDBユーザーを指定する方式です。

なおどちらの認証方式を選択したとしても、RDS Proxy〜RDS間の接続はSecret Managerのシークレット(ユーザー名・パスワード)を使用する認証となります。

今回は2つの認証方式を比較する形で確認します。

構築する環境

AWS認定資格試験テキスト AWS認定ソリューションアーキテクト - アソシエイト 改訂第2版
AWSを使って安全で堅牢なアプリケーションの設計およびデプロイできる知識をもっているかを証明するのが、「AWS認定 ソリューションアーキテクト - アソシエイト」試験です。 本書は、2020年に改訂された新しい「SAA-C02」試験に対応した改訂を行った試験対策教科書です。合格に必要な「レジリエントアーキテクチャの設計...
Diagram of two authentication methods for RDS Proxy(Password/IAM)

基本的な構成は先述のページと同様です。
異なる点は2つの認証用として、2つのRDS Proxyを作成している点と、それらを使用するLambda関数を用意している点です。

今回作成する構成の挙動を簡単に説明します。
DBインスタンスに日時データを保存するテーブルを作成します。
API GatewayにHTTPリクエストが届くと、バックエンドのLambdaが呼び出され、現在日時を取得します。
取得した日時データをRDS Proxy経由でDBインスタンスに保存します。
加えてDBインスタンスに保存されている全データを取得し、HTTPレスポンスとして返します。

CloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。

awstut-fa/039 at main · awstut-an-r/awstut-fa
Contribute to awstut-an-r/awstut-fa development by creating an account on GitHub.

テンプレートファイルのポイント解説

基本的な構成は以下のページと同様です。

本ページでは、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」アクションを許可する内容です。

こちらのポリシーは以下のページを参照して定義しました。

IAM データベースアクセス用の IAM ポリシーの作成と使用 - Amazon Relational Database Service
IAM ユーザーまたはロールに DB インスタンス への接続を許可するには、IAM ポリシーを作成する必要があります。その後、そのポリシーを IAM ユーザーまたはロールに関連付けます。

関数の中身を確認します。
特に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認証時のオブジェクト作成を実装します。

IAM 認証および AWS SDK for Python (Boto3) を使用した DB インスタンスへの接続 - Amazon Relational Database Service
次に説明するように、AWS SDK for Python (Boto3) を使用して、 RDS for MariaDB、MySQL、または PostgreSQL DB インスタンス に接続できます。

ポイントは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の認証に関する項目を確認します。

RDS Proxy 1 authentication method.
RDS Proxy 2 authentication method.

それぞれパスワード認証、IAM認証として設定したRDS Proxyです。

動作確認

準備が整いましたので、API Gatewayにアクセスします。

まずパスワード認証で接続するLambda関数の動作を確認します。
/func1が本関数のURLです。

The Result of Lambda function 1.

正常にレスポンスが返ってきました。
パスワード認証で接続できることを確認しました。

続いてIAM認証で接続するLambda関数の動作を確認します。
/func2が本関数のURLです。

The Result of Lambda function 2.

正常にレスポンスが返ってきました。
IAM認証で接続できることを確認しました。

まとめ

LambdaからRDS Proxy経由でDBインスタンスに接続する際の、2つの認証方式を確認しました。

タイトルとURLをコピーしました