Enforcing TLS and managing certificate rotation for RDS and Amazon Aurora PostgreSQL

When an Amazon Relational Database Service (Amazon RDS) certificate expires and the application’s trust store remains outdated, it can lead to unexpected connection failures. Teams often become aware of these issues only when users report errors, rather than proactively preventing them. Although AWS announces Certificate Authorities (CA) rotation events well in advance and offers tools for management, a lack of proactive certificate lifecycle management can result in unplanned downtime across production workloads during these rotations.

The implications extend beyond operational challenges. Default configurations for RDS and Amazon Aurora permit both encrypted and unencrypted PostgreSQL connections. Without proper configuration, unencrypted database traffic may flow undetected, potentially exposing sensitive data in transit and compromising security based on your configuration choices. While AWS automatically provisions Transport Layer Security (TLS) certificates and provides enforcement mechanisms like rds.force_ssl=1, explicit enabling is necessary based on security requirements. Even organizations that enforce TLS often lack visibility into which connections are encrypted and whether their applications are ready for the next CA rotation.

According to the AWS shared responsibility model, AWS manages the Certificate Authority infrastructure and provisions server certificates for each instance. However, the responsibility for enforcing TLS usage, configuring client verification, and managing the rotation lifecycle according to security needs falls to the user.

This guide will demonstrate how to enforce TLS encryption for all PostgreSQL connections on AWS, configure client-side certificate verification, and establish automated monitoring for expiring certificates 30 days prior to certificate rotation events.

How TLS Works in RDS and Aurora

Amazon RDS employs a managed Certificate Authority hierarchy (rds-ca) that issues server certificates for each database instance. The TLS handshake process is as follows:

  1. The client initiates a connection to the RDS endpoint.
  2. The server presents its certificate issued by the rds-ca authority.
  3. The client validates the certificate against its local trust store (if sslmode is set to verify-ca or verify-full).
  4. The client and server negotiate a cipher suite and establish an encrypted channel.
  5. All subsequent data is transmitted over the encrypted connection.

Amazon RDS for PostgreSQL and Aurora PostgreSQL support TLS 1.2 and TLS 1.3, with the actual version depending on the capabilities of the client library and the PostgreSQL engine version.

Solution Overview

The solution comprises three layers:

  1. Server-side enforcement: The rds.force_ssl parameter ensures that any connection attempt not utilizing TLS is rejected.
  2. Client-side verification: The sslmode=verify-full connection parameter allows clients to validate the server’s identity against a trusted CA bundle.
  3. Automated lifecycle monitoring: Utilizing Amazon EventBridge, AWS Lambda, and Amazon CloudWatch, alerts can be generated before certificates expire.

Key services involved include Amazon RDS for PostgreSQL, Amazon Aurora PostgreSQL, Amazon EventBridge, AWS Lambda, Amazon CloudWatch, and AWS Identity and Access Management (IAM).

The benefits of this approach are:

  1. Server-side enforcement to block plaintext connections.
  2. Automated rotation to help avoid unplanned downtime.
  3. Continuous compliance validation through Amazon EventBridge rules.

Prerequisites

To follow along with this guide, ensure you have the following prerequisites:

  1. An AWS account with an Amazon RDS for PostgreSQL or Amazon Aurora PostgreSQL instance (engine version 14 or later).
  2. IAM permissions for Amazon RDS, AWS Lambda, Amazon EventBridge, and Amazon CloudWatch.
  3. A PostgreSQL client (psql 14 or later recommended).
  4. Familiarity with Amazon RDS parameter groups.
  5. The openssl CLI (for validation steps).

Step 1: Enforcing TLS on the Server Side

The rds.force_ssl parameter operates at the server level, controlling whether Amazon RDS rejects non-SSL connections. For PostgreSQL version 15 and later, rds.force_ssl defaults to 1 (on), while earlier versions default to 0 (off).

Create or Modify a Parameter Group (Only if Version is <=14)

Default parameter groups are immutable, so you must create a custom parameter group first.

aws rds create-db-parameter-group 
    --db-parameter-group-name my-parameter-group 
    --db-parameter-group-family postgres14 
    --description "PostgreSQL parameter group with TLS enforcement"

Enable rds.force_ssl (Only if Version is <=14)

Set rds.force_ssl=1 in the custom parameter group to reject all non-SSL connections. Use ApplyMethod=pending-reboot to ensure the change takes effect after the next instance reboot.

aws rds modify-db-parameter-group 
    --db-parameter-group-name my-parameter-group 
    --parameters "ParameterName=rds.force_ssl,ParameterValue=1,ApplyMethod=pending-reboot"

Apply the Parameter Group to Your Instance (Only if Version is <=14)

Attach the custom parameter group to the target RDS instance. Using --apply-immediately applies the parameter group association right away, but the rds.force_ssl value requires a reboot to take effect.

aws rds modify-db-instance 
    --db-instance-identifier my-postgres-instance 
    --db-parameter-group-name my-parameter-group 
    --apply-immediately

Aurora PostgreSQL Differences

For Aurora PostgreSQL, the parameter operates similarly but is configured through a DB cluster parameter group instead of a DB instance parameter group. Use modify-db-cluster-parameter-group for this purpose.

Step 2: Certificate Authority Options

Amazon RDS provides three current Certificate Authority (CA) options, each designed to balance cryptographic strength, performance, and longevity. A significant operational advantage of all three is that Amazon RDS automatically rotates the DB server certificate before it expires, minimizing manual renewal tasks.

The first option, rds-ca-rsa2048-g1, utilizes an RSA key with a 2048-bit key size, making it the most compatible choice and a solid baseline for most workloads. Its CA certificate has a 40-year validity window, providing decades before the root itself needs replacement.

If stronger cryptographic assurance is required, rds-ca-rsa4096-g1 doubles the RSA key size to 4096 bits, making brute-force attacks exponentially more difficult, with a CA validity of 100 years. This effectively future-proofs the trust anchor for the lifetime of your infrastructure.

For teams favoring elliptic-curve cryptography, rds-ca-ecc384-g1 offers comparable security to RSA-4096 with smaller keys and faster handshakes. At 384 bits on the ECC curve, it provides robust protection while reducing computational overhead, making it particularly appealing for high-throughput or latency-sensitive workloads. This option also carries a 100-year validity period.

Regardless of the CA selected, the automatic server certificate rotation ensures that day-to-day certificate lifecycle management remains hands-off. Your primary responsibility is to ensure that your client trust store includes the appropriate CA bundle to validate the new server certificates as they are rolled out.

CA Identifier Key Type Key Size Validity Auto Server Cert Rotation
rds-ca-rsa2048-g1 RSA 2048 bits 40 years Yes
rds-ca-rsa4096-g1 RSA 4096 bits 100 years Yes
rds-ca-ecc384-g1 ECC 384 bits 100 years Yes

Check Your Current CA

Query the current CA certificate assigned to your RDS instance to confirm it is one of: rds-ca-rsa2048-g1, rds-ca-rsa4096-g1, or rds-ca-ecc384-g1. If it shows rds-ca-2019, you must migrate to a supported CA.

aws rds describe-db-instances 
    --db-instance-identifier my-postgres-instance 
    --query 'DBInstances[0].CACertificateIdentifier'

Apply a New CA (If Needed)

Change the CA assigned to the instance to one of the supported options. Using --apply-immediately triggers the CA swap immediately, which may cause a brief connectivity interruption.

aws rds modify-db-instance 
    --db-instance-identifier my-postgres-instance 
    --ca-certificate-identifier rds-ca-rsa2048-g1 
    --apply-immediately

Step 3: Client-side Certificate Verification

Understanding sslmode Options

When connecting to a PostgreSQL database, the sslmode parameter serves as the primary control for the security of that connection. It determines whether encryption is employed, whether the server’s certificate is validated, and whether the hostname on the certificate matches the intended server. Each increment in security adds assurance but also configuration responsibility.

At the lowest level, disable offers no encryption, no verification, and no protection, allowing traffic to flow in plaintext and vulnerable to interception. The next levels, allow and prefer, negotiate encryption: they will use TLS if the server supports it but do not insist on it or verify the server’s identity. The distinction lies in allow, which starts unencrypted and upgrades only if necessary, while prefer (the default in most client libraries) attempts TLS but falls back to plaintext if it fails. Both modes offer no guarantee of encryption and no protection against impersonation.

With require, encryption becomes mandatory, and the connection fails rather than reverting to plaintext. However, the client does not verify the server’s identity, leaving room for an on-path attacker to intercept traffic with any valid-looking certificate. Transitioning to verify-ca adds certificate authority validation, ensuring the server’s certificate was signed by a trusted CA, which mitigates most impersonation scenarios but may still allow for a compromised certificate issued to the wrong hostname. Finally, verify-full closes this gap by also checking that the hostname on the certificate matches the server you’re connecting to, making it the most secure option and the recommended setting for production workloads handling sensitive data.

sslmode Encryption CA Verification Hostname Check Protection Level
disable No No No None
allow Negotiated No No Minimal
prefer (default) Negotiated No No Opportunistic
require Yes No No Encryption only
verify-ca Yes Yes No CA verified
verify-full Yes Yes Yes Full (recommended)

Downloading the RDS CA Bundle

Combined Bundle (All AWS Regions)

Download the global CA bundle, which includes all AWS regions. This file contains the complete certificate chain for all RDS/Aurora instances worldwide and should be used unless region-specific bundles are desired.

curl -o global-bundle.pem https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem

Region-Specific Bundle (Example: eu-west-1)

To download only the CA bundle for a specific region, replace eu-west-1 with the AWS region where your RDS instance is located.

curl -o eu-west-1-bundle.pem https://truststore.pki.rds.amazonaws.com/eu-west-1/eu-west-1-bundle.pem

Verify the Downloaded Bundle

Inspect the first certificate in the bundle to confirm the issuer is Amazon RDS. Look for Issuer: CN=Amazon RDS Root… in the output.

openssl x509 -in global-bundle.pem -text -noout | head -20

Step 4: Certificate Rotation Strategy

Certificates rotate for two primary reasons: CA expiry (when the root CA reaches its validity end date) and security hygiene (AWS periodically rotates server certificates to minimize the risk from key compromise).

Rotation Workflow

A safe rotation follows this sequence across environments:

  1. Update client trust stores with the new CA bundle, including both old and new CAs during the transition period.
  2. Test connectivity in development/staging with the new bundle.
  3. Modify the DB instance to use the new CA: aws rds modify-db-instance --db-instance-identifier my-postgres-instance --ca-certificate-identifier rds-ca-rsa2048-g1 --apply-immediately.
  4. Validate connections post-rotation through pg_stat_ssl.
  5. Roll out to production following the same sequence: trust store first, then CA rotation.
  6. Remove the old CA from trust stores after confirming all instances are using the new CA.

Critical: Update client trust stores before rotating the CA on the instance to avoid connection failures. Rotating the server CA first may cause clients with outdated trust stores to fail to connect immediately.

AWS-Initiated and Customer-Initiated Rotation

With the new CAs (rds-ca-rsa2048-g1, rsa4096-g1, ecc384-g1), AWS is designed to automatically rotate the server certificate before expiry. The CA bundle in your client trust store is intended to remain unchanged, with only the leaf server certificate rotating. No client-side action is necessary for automatic server certificate rotation.

Customer-initiated rotation is required when migrating from the rds-ca-2019 to a newer CA, switching between CA types (e.g., RSA to ECC), or if your organization’s security policy mandates periodic CA changes.

Step 5: Validating Encryption in Transit

Query pg_stat_ssl

Utilize pg_stat_ssl to review the TLS status for all active connections:

  • ssl=true: the connection is encrypted.
  • ssl=false: the connection is plaintext.

The tls_version field indicates whether TLSv1.2 or TLSv1.3 is negotiated for each connection.

Confirm No Plaintext Connections

Count active connections where ssl=false (unencrypted). With rds.force_ssl=1 enabled, this count should typically return 0. A non-zero count may indicate that some clients are bypassing TLS, warranting further investigation.

aws rds modify-db-parameter-group 
    --db-parameter-group-name my-parameter-group 
    --parameters "ParameterName=rds.force_ssl,ParameterValue=1,ApplyMethod=pending-reboot"

Test with openssl s_client

Use openssl to test the full TLS handshake from a client machine. The -starttls postgres option triggers the PostgreSQL STARTTLS upgrade before the TLS handshake, while -CAfile specifies the trusted CA bundle for certificate chain validation.

openssl s_client -starttls postgres -CAfile global-bundle.pem -h your-db-host -p your-db-port

Look for Verify return code: 0 (ok) in the output, confirming the validity of the certificate chain.

Step 6: Automating Monitoring Expiry

To proactively manage certificate expirations, automated monitoring is essential. This involves periodically scanning every RDS instance in your account and raising alerts when any certificate nears its end of life or is still using the deprecated rds-ca-2019 CA.

Rather than manually wiring up individual AWS resources through CLI commands, we provide an AWS Cloud Development Kit (AWS CDK) project that deploys the complete certificate monitoring stack as a single, reproducible infrastructure-as-code deployment. The CDK project provisions the Lambda function, Amazon EventBridge rules (for both daily schedules and maintenance event listeners), the IAM execution role with least-privilege permissions, and a CloudWatch alarm, all with a single cdk deploy command. The source code is available on GitHub.

Deploying the Stack

Clone the repository and deploy:

aws rds modify-db-parameter-group 
    --db-parameter-group-name my-parameter-group 
    --parameters "ParameterName=rds.force_ssl,ParameterValue=1,ApplyMethod=pending-reboot"

0

How It Works

After deployment, the monitoring solution operates in two complementary modes:

  • Proactive (Scheduled Scan): Every day, the Amazon EventBridge schedule invokes the Lambda function, which enumerates all RDS instances, queries each one’s CA certificate metadata, calculates days until expiry, and publishes the ExpiringCertificates custom metric to CloudWatch. If the count is ≥ 1, the CloudWatch alarm transitions to ALARM state.
  • Reactive (Event-Driven): When AWS initiates a certificate rotation on any instance (events RDS-EVENT-0501 or RDS-EVENT-0502), the Amazon EventBridge maintenance rule triggers the Lambda, providing near-real-time awareness of ongoing rotation activities.

Cleaning Up

Since CDK manages all resources, cleanup requires a single command:

aws rds modify-db-parameter-group 
    --db-parameter-group-name my-parameter-group 
    --parameters "ParameterName=rds.force_ssl,ParameterValue=1,ApplyMethod=pending-reboot"

1

Common Pitfalls and Troubleshooting

Even with a robust certificate rotation plan, several issues may catch teams off guard, particularly during or immediately after a change. Most stem from mismatches between server expectations and client configurations, such as outdated trust stores, Object-Relational Mapping (ORM) frameworks overriding SSL settings, or applications that were never explicitly configured for encryption suddenly being forced into it. Performance concerns after enabling TLS are also common, though they are typically more perceived than severe once connection pooling is implemented.

Problem Cause Solution
Connections fail after rotation Client trust store contains only the previous CA bundle Update trust store with the combined bundle (includes both old and new CAs) before rotating
Application ignores sslmode setting Some ORMs override connection parameters Set PGSSLMODE environment variable as a fallback. Review framework-specific SSL configuration
Performance degradation after enabling TLS TLS adds ~1-3ms per connection establishment Use connection pooling to amortize handshake cost. For ongoing data, overhead is typically < 5% CPU
Cannot connect after enabling rds.force_ssl Application was using unencrypted connections Add sslmode=require (minimum) or verify-full to all connection strings before enabling force_ssl

Summary and Checklist

Before declaring your RDS for PostgreSQL deployment production-ready from a TLS perspective, walk through this checklist. Each item represents a layer in your defense-in-depth strategy; skipping any could result in connection failures, security audit findings, or worse, unencrypted data in transit.

  1. rds.force_ssl=1 enabled in parameter group: ensures the server rejects any unencrypted connection attempts.
  2. Client connections use sslmode=verify-full: guarantees encryption and server identity verification from the application side.
  3. Current CA applied (rds-ca-rsa2048-g1, rsa4096-g1, or ecc384-g1): confirms migration from the deprecated rds-ca-2019 certificate authority.
  4. CA bundle distributed to all application servers: every client must trust the CA that signed the server certificate.
  5. pg_stat_ssl confirms zero unencrypted connections: your validation step. Any row showing ssl=false indicates a gap.
  6. Amazon EventBridge and Lambda monitor certificate expiry: automated scanning eliminates reliance on human memory or calendar reminders.
  7. CloudWatch alarm configured for expiry threshold: turns monitoring data into actionable notifications before issues arise.
  8. Rotation tested in non-production environment: never let production be the first place you discover a trust store mismatch.
Tech Optimizer
Enforcing TLS and managing certificate rotation for RDS and Amazon Aurora PostgreSQL