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:
- The client initiates a connection to the RDS endpoint.
- The server presents its certificate issued by the
rds-caauthority. - The client validates the certificate against its local trust store (if
sslmodeis set toverify-caorverify-full). - The client and server negotiate a cipher suite and establish an encrypted channel.
- 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:
- Server-side enforcement: The rds.force_ssl parameter ensures that any connection attempt not utilizing TLS is rejected.
- Client-side verification: The sslmode=verify-full connection parameter allows clients to validate the server’s identity against a trusted CA bundle.
- 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:
- Server-side enforcement to block plaintext connections.
- Automated rotation to help avoid unplanned downtime.
- Continuous compliance validation through Amazon EventBridge rules.
Prerequisites
To follow along with this guide, ensure you have the following prerequisites:
- An AWS account with an Amazon RDS for PostgreSQL or Amazon Aurora PostgreSQL instance (engine version 14 or later).
- IAM permissions for Amazon RDS, AWS Lambda, Amazon EventBridge, and Amazon CloudWatch.
- A PostgreSQL client (psql 14 or later recommended).
- Familiarity with Amazon RDS parameter groups.
- 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:
- Update client trust stores with the new CA bundle, including both old and new CAs during the transition period.
- Test connectivity in development/staging with the new bundle.
- 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. - Validate connections post-rotation through
pg_stat_ssl. - Roll out to production following the same sequence: trust store first, then CA rotation.
- 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
ExpiringCertificatescustom 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-0501orRDS-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.
rds.force_ssl=1enabled in parameter group: ensures the server rejects any unencrypted connection attempts.- Client connections use
sslmode=verify-full: guarantees encryption and server identity verification from the application side. - Current CA applied (
rds-ca-rsa2048-g1,rsa4096-g1, orecc384-g1): confirms migration from the deprecatedrds-ca-2019certificate authority. - CA bundle distributed to all application servers: every client must trust the CA that signed the server certificate.
pg_stat_sslconfirms zero unencrypted connections: your validation step. Any row showingssl=falseindicates a gap.- Amazon EventBridge and Lambda monitor certificate expiry: automated scanning eliminates reliance on human memory or calendar reminders.
- CloudWatch alarm configured for expiry threshold: turns monitoring data into actionable notifications before issues arise.
- Rotation tested in non-production environment: never let production be the first place you discover a trust store mismatch.