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

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

RDS Proxyの2つの認証方式

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

あわせて読みたい
VPC内のLambdaからRDS Proxy経由でRDSに接続する 【VPC内のLambdaからRDS Proxy経由でRDSに接続する】 VPC内にLambdaを配置し、RDSに接続する構成を考えます。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つの認証方式を比較する形で確認します。

構築する環境

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テンプレートを配置しています。

https://github.com/awstut-an-r/awstut-fa/tree/main/039

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

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

あわせて読みたい
VPC内のLambdaからRDS Proxy経由でRDSに接続する 【VPC内のLambdaからRDS Proxy経由でRDSに接続する】 VPC内にLambdaを配置し、RDSに接続する構成を考えます。LambdaからRDSにアクセスする際は、直接接続するのではなく...

本ページでは、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レイヤーの詳細につきましては、以下のページをご確認ください。

あわせて読みたい
CFNでLambdaレイヤー作成 【CloudFormationでLambdaレイヤー作成】 本ページでは、CloudFormationでLambdaレイヤーを作成する方法を確認します。 Lambda レイヤーは、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認証時のオブジェクト作成を実装します。

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.Connecting.Python.html

ポイントは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スタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。

あわせて読みたい
CloudFormationのネストされたスタックで環境を構築する 【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つの認証方式を確認しました。