Hire Me
← All Writing AWS

RDS PostgreSQL with IAM Authentication and SSL in Spring Boot

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.

How IAM authentication works

IAM authentication replaces the database password with a token generated from AWS credentials:

  1. Your application calls the AWS RDS API to generate an authentication token
  2. The token is valid for 15 minutes
  3. The token is used as the password in the JDBC connection string
  4. RDS validates the token against IAM — the IAM principal must have the rds-db:connect permission

The database user must also be configured to use IAM authentication:

CREATE USER trading_app WITH LOGIN;
GRANT rds_iam TO trading_app;

IAM policy for the task role

{
  "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");

Token generation in Spring Boot

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.

SSL configuration

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 development

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: ""

Verifying the connection

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.

Samuel Jackson

Samuel Jackson

Senior Java Back End Developer & Contractor

Senior Java Back End Developer — Betfair Exchange API specialist, Spring Boot, AWS, and event-driven architecture. 20+ years delivering high-performance systems across betting, finance, energy, retail, and government. Available for Java contracting.