Secure Connection between Lambda and RDS: Choosing and Implementing SSL/TLS Certificates
Tags: webdev, aws, security, database
Published: 2023-11-23 06:10:15

Recently, while integrating an RDS Read Replica into our architecture, we encountered the following error message when setting up an SSL/TLS connection from Lambda (NestJS) to the RDS (Read Replica):

```objectivec
Error: self-signed certificate in certificate chain
```
This article documents the cause of this problem and how it was resolved.

## Cause
The issue was simply due to the use of an inappropriate certificate.

### Certificate for RDS Proxy
Originally, our setup involved connecting from Lambda (NestJS) through RDS Proxy to the primary RDS. In this case, we used the [AmazonRootCA1.pem](https://www.amazontrust.com/repository/AmazonRootCA1.pem) certificate.

When using RDS Proxy, AWS's `AmazonRootCA1.pem` root certificate is crucial. This certificate is employed to verify the identity of RDS Proxy and ensure the encryption of communications.

```
Lambda(NestJS) → RDS Proxy → RDS (Primary)
```

https://repost.aws/knowledge-center/rds-aurora-mysql-connect-proxy

### Direct Connection to RDS (Read Replica)
For the newly added RDS (Read Replica) connection, it was necessary to connect directly from Lambda without going through the RDS Proxy. (As RDS Proxy is primarily used for load reduction in write operations and efficient connection management, it was not needed for the read-only Read Replica, so a direct connection was chosen.)

For this direct connection, a region-specific `ap-northeast-1-bundle.pem(~-bundle.pem)` certificate was required.

```
Lambda(NestJS) → RDS (Read Replica)
```

## Solution
To resolve this issue, we used the appropriate SSL/TLS certificate for the connection to the RDS (Read Replica).

We referred to the official AWS page for the certificate bundle `ap-northeast-1-bundle.pem`, which resolved the error.

https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html

## Supplement: Method for Loading Certificates (NestJS)
For loading the SSL/TLS certificates in the application (NestJS), we implemented a method to retrieve the certificates from AWS S3.

### Certificate Retrieval Process from S3
We defined a `S3CertificatesService` class and accessed S3 using the AWS SDK. This class provides a `getCertificateFromS3` method to retrieve certificates from a specified bucket and key.

```ts
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 Connection Configuration
For the DB connection settings, we used the above service to retrieve the certificate from S3 and applied it to our DB connection configuration. Here, we obtained the `ap-northeast-1-bundle.pem` certificate and used it for the SSL connection to the DB (MySQL).

```ts
    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
      }
    };
```