How to connect a Spring Boot service to RDS PostgreSQL using IAM database authentication instead of a static password — eliminating credentials from configuration and enabling automatic rotation.
Static database passwords in Spring Boot configuration — even when loaded from Secrets Manager — require rotation coordination between your application and your database. IAM authentication for RDS eliminates the password entirely. Your ECS task role or EC2 instance profile generates a short-lived token on demand, which RDS validates against IAM. No password to rotate, no password to leak.
IAM authentication replaces the database password with a token generated from AWS credentials:
rds-db:connect permissionThe database user must also be configured to use IAM authentication:
CREATE USER trading_app WITH LOGIN;
GRANT rds_iam TO trading_app;
{
"Effect": "Allow",
"Action": ["rds-db:connect"],
"Resource": [
"arn:aws:rds-db:eu-west-2:123456789:dbuser:db-ABC123DEF456/trading_app"
]
}
The resource ARN format: arn:aws:rds-db:{region}:{account}:dbuser:{db-resource-id}/{db-user}. The db-resource-id is the RDS instance’s resource ID (starts with db-), not the instance identifier.
In CDK:
cluster.grantConnect(taskRole, "trading_app");
Generate a new token before establishing each connection. HikariCP calls the DataSource for each new connection — override the password property with a freshly generated token.
The easiest approach: a custom DataSource wrapper that generates the token:
public class IamAuthDataSource extends HikariDataSource {
private final RdsUtilities rdsUtilities;
private final String hostname;
private final String username;
private final int port;
private final String region;
public IamAuthDataSource(HikariConfig config, String region) {
super(config);
this.region = region;
this.hostname = config.getJdbcUrl(); // extract from URL in practice
this.username = config.getUsername();
this.port = 5432;
this.rdsUtilities = RdsUtilities.builder()
.region(Region.of(region))
.build();
}
@Override
public Connection getConnection() throws SQLException {
setPassword(generateToken());
return super.getConnection();
}
private String generateToken() {
return rdsUtilities.generateAuthenticationToken(t -> t
.hostname(hostname)
.port(port)
.username(username)
.region(Region.of(region)));
}
}
A cleaner approach: configure HikariCP with a connection initialiser that sets the token via the JDBC URL or properties map:
@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource(
@Value("${spring.datasource.url}") String url,
@Value("${spring.datasource.username}") String username,
@Value("${aws.region}") String region
) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
config.setUsername(username);
config.setConnectionInitSql("SELECT 1");
config.setMaximumPoolSize(10);
config.addDataSourceProperty("password", generateToken(url, username, region));
// Rotate the token before connections expire (tokens last 15min)
config.setMaxLifetime(Duration.ofMinutes(14).toMillis());
return new HikariDataSource(config);
}
}
Setting maxLifetime to 14 minutes ensures connections are replaced before the 15-minute token expires.
IAM authentication requires SSL — connections without SSL are rejected. Configure it in the JDBC URL:
spring:
datasource:
url: >
jdbc:postgresql://trading-db.abc123.eu-west-2.rds.amazonaws.com:5432/trading
?ssl=true
&sslmode=verify-full
&sslrootcert=/etc/ssl/certs/aws-rds-ca.pem
Download the AWS RDS CA certificate bundle:
curl -o aws-rds-ca.pem \
https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem
Include it in your Docker image:
COPY aws-rds-ca.pem /etc/ssl/certs/aws-rds-ca.pem
sslmode=verify-full validates the server certificate’s hostname — prevents man-in-the-middle attacks. Do not use sslmode=require alone, which validates the certificate but not the hostname.
Local developers don’t have the task role. Options:
Option 1 — AWS SSO: Developers authenticate with aws sso login. If they have rds-db:connect permission on the dev database, IAM auth works locally with the standard credential chain.
Option 2 — Local database: Override the datasource in application-local.yml with a local PostgreSQL instance and standard username/password. No IAM token generation in local dev.
# application-local.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/trading
username: trading_app
password: localpassword
hikari:
connection-init-sql: ""
Enable HikariCP debug logging to confirm token generation:
logging:
level:
com.zaxxer.hikari: DEBUG
You should see “Connection acquired from pool” without any authentication errors. If you see “FATAL: PAM authentication failed”, the IAM policy is missing or the RDS instance does not have IAM authentication enabled.
Enable IAM authentication on the RDS instance:
// CDK
DatabaseInstance.Builder.create(this, "TradingDb")
.iamAuthentication(true)
.build();
IAM authentication removes one class of credential from your configuration entirely — the database password simply does not exist as a secret to rotate, protect, or leak.
If you’re securing RDS connections in a Spring Boot AWS deployment and want a review, get in touch.