見出し画像

LambdaからRDSリードレプリカへの暗号化接続で起こったSSL証明書エラーの解決法

こんにちは、すずきです。

最近、アーキテクチャにRDSのリードレプリカを組み込む作業を行っているのですが、Lambda(NestJS)からRDS(リードレプリカ)へのSSL/TLS接続を設定する際、以下のエラーメッセージが表示されました。

Error: self-signed certificate in certificate chain

一瞬ハマったので、問題の原因と解決方法を備忘録として残します。


原因

単純に、使用する証明書が不適切だったのが原因でした。

RDS Proxyを介する場合の証明書

元々の構成では、Lambda(NestJS)からRDS Proxyを経由してRDS(プライマリ)に接続しており、この場合にはAmazonRootCA1.pem証明書を使用していました。

RDS Proxyを使用する際には、AWSが提供するAmazonRootCA1.pemというルート証明書が重要です。この証明書は、RDS Proxyの身元確認と通信の暗号化を保証するために用いられます。

Lambda(NestJS) → RDS Proxy → RDS(プライマリ)

RDS(リードレプリカ)への直接接続

今回新たに追加したRDS(リードレプリカ)への接続では、RDS Proxyを介さず直接Lambdaから接続する必要がありました(RDS Proxyは主に書き込み操作の負荷を軽減し、接続管理を効率化するために使用されますが、読み取り専用のリードレプリカではこれが必要ないため、直接接続を選択しました)。

この直接接続では、リージョンに特化したap-northeast-1-bundle.pem(~-bundle.pem)証明書が必要でした。

Lambda(NestJS) → RDS(リードレプリカ)

解決方法

この問題を解決するために、RDS(リードレプリカ)への接続で適切なSSL/TLS証明書を使用しました。

AWSの公式ページに証明書のバンドルap-northeast-1-bundle.pemがあったので、それを参照し直すことでエラーが解消されました。

補足:証明書の読み込み方法(NestJS)

アプリケーション(NestJS)でのSSL/TLS証明書の読み込みは、AWS S3から証明書を取得する形で実装しました。

S3からの証明書取得処理

S3CertificatesServiceクラスを定義し、AWS SDKを使用してS3にアクセスします。このクラスでは、指定されたバケットとキーから証明書を取得するgetCertificateFromS3メソッドを提供します。

import { Injectable } from '@nestjs/common'
import * as AWS from 'aws-sdk'

@Injectable()
export class S3CertificatesService {
  private s3: AWS.S3

  constructor() {
    this.s3 = new AWS.S3()
  }

  async getCertificateFromS3(bucket: string, key: string): Promise<string> {
    const params = {
      Bucket: bucket,
      Key: key
    }

    const data = await this.s3.getObject(params).promise()

    if (data.Body) {
      return data.Body.toString()
    }

    throw new Error('Failed to retrieve certificate from S3. Body is undefined.')
  }
}

DB接続設定

DBへの接続設定では、上記サービスを利用してS3から証明書を取得し、DB接続の設定に適用します。ここではap-northeast-1-bundle.pem証明書を取得し、DB(MySQL)へのSSL接続に使用しています。

    const certificate = await s3CertificatesService.getCertificateFromS3('rds-certificates', 'ap-northeast-1-bundle.pem')
    const dbConfig = {
      type: 'mysql',
      host: '',
      port: 3306,
      username: '',
      database: '',
      password: '',
      ssl: {
        ca: certificate,
        rejectUnauthorized: true
      }
    }

余談

ず〜〜〜〜〜っとつくってほしいと願っていたOPTEMO【公式】noteアカウントがようやくできたよ。
これで会社としての発信活動が活発になるといいよね。

一発目の記事がかなり練って書かれていてよかったので、多くの方にご覧いただけるとうれしい限りだよ。


この記事が気に入ったらサポートをしてみませんか?