Skip to main content

How to Implement AWS RDS Database IAM Authentication in Spring Boot

Amazon RDS for MySQL allows authentication using AWS Identity and Access Management (IAM) database authentication. With this authentication method, you don't need to use a password when you connect to a DB instance. Instead, you use an authentication token.

Let us understand how this works?

An authentication tokenis a unique string of characters that Amazon RDS generates on request. Authentication tokens are generated using AWS Signature Version 4. Each token has a lifetime of 15 minutes. You don't need to store user credentials in the database, because authentication is managed externally using IAM. You can also still use standard database authentication.

Since IAM authentication tokens are short-lived access tokens that are valid for 15 minutes. For the RDS database this token works as a database password that is required to establish a connection and does not determine how long the existing connection can last. The default value for connection to be alive without activity is 28800s (8 hours) as explained here.

This means that if you terminate your connection, or MySQL terminates it due to not being actively used, then you need to get a new token to re-connect if the existing one already expired.

Short-lived database credentials are a best practice because they have a limited “blast radius” if compromised. However, Spring Boot — indeed, Java connection pools, in general, aren’t well-adapted to changing credentials, because they’re normally configured when the application starts. To use short-lived credentials, you must have to refresh those credentials before it expires. Follow the below steps to learn how we refresh database credentials in runtime in the spring boot application.

RDS Iam authentication

To connect your Spring Boot Application with the AWS RDS database using IAM authentication, the following steps :

  1. Creating an IAM user with programmatic access.
  2. Attach an inline policy with the user to access the RDS instance.
  3. Setup user access key and secret in the application environment.
  4. Create or modify RDS instance allowing Password and IAM authentication.
  5. Setup database to use IAM.
  6. Implement the application.

1. Creating an IAM user with programmatic access.

Assuming that you have an account on AWS and an IAM user, if not then log in to your AWS account and go to the IAM users page using the link https://console.aws.amazon.com/iam/home#/users.  Then click on "Add users" and follow the steps shown, it's pretty simple, don't forget to allow Programmatic access as Access type. At finishing it will ask you to download a CSV file that will contain your access key and secret key, Keep it safe we will use it further.

Now the main thing that you need to focus on is that the newly created user has permission to connect rds database?

2. Attach an inline policy with the user to access the RDS instance

For this go to "users" then select your "user_name" and under the "Permissions" tab click on "Add inline policy" as shown in the below image.

Inline policy for RDS connect

After clicking on "Add inline policy" you will have to add the following JSON policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "rds-db:connect",
                "rds-db:*"
            ],
            "Resource": "*"
        }
    ]
}

Copy the above JSON  and paste as shown in the below image.

inline policy editor for rds connect

Next click "Review policy" then in the next step click on "Save changes".

3. Setup user access key and secret in the application environment.

If your application executes in an AWS instance directly then you do not need to do this step. But if you want to test this functionality from another system like from your local machine then this step is required.

Create a text file without an extention named "credentials" and paste this in .aws  folder in home directory. This file could be found with the following path

In Linux: /home/<username>/.aws/credentials

In Windows: C:\users\<username>\.aws\credentials

In Mac: /users/<username>/.aws/credentials

credentials file content:

[default]
aws_access_key_id = AKIAVGOA4***********
aws_secret_access_key = VJEr+DHzzFESsUgp*********************

4. Create or modify RDS instance allowing Password and IAM authentication.

rds allow password and iam authentication

5. Setup database to use IAM.

Log in to your RDS database using your master username and password that you created while creating the RDS instance. You can get the endpoint and port number in the connectivity & security tab from your DB identifier as shown in the below image.

RDS DB Identifier

If you are allowed to access the RDS database publically you can log in from any system with your credentials, otherwise, you can only log in from your AWS instance or from the IP addresses that are allowed in inbound/outbound rules of your security groups that are attached to your RDS instance.

For the testing purpose, I have set "Public access" to yes (You can also do this in Step 4).

Connect to your RDS database using the following command:

$mysql --host easytutorials-demords.chgtv6yotna4.ap-south-1.rds.amazonaws.com --port 3306 -u admin -p

In the next step, you will have to enter your master password.

After successful login, run the following queries.

CREATE USER 'lavkush_rdsuser' IDENTIFIED WITH AWSAuthenticationPlugin as 'RDS'  REQUIRE SSL;

The username 'lavkush_rdsuser' must be the same as your IAM user's username created in step 1.

GRANT ALL PRIVILEGES ON rdsiamtestdb.* TO 'lavkush_rdsuser'@'%';

Here, rdsiamtestdb  is the database name on which all permissions will be granted to the user.

6. Implement the application

Here is the step-by-step implementation of RDS IAM authentication in spring boot. You will require AWS java SDK for RDS for generating authentication tokens. Others are common.

In this demo application, there are two application profiles i.e default and prod. The default profile is for the local environment and prod is for production. To understand how spring boot profiling works, click this link.

6.1 build.gradle

plugins {
    id 'org.springframework.boot' version '2.5.2'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'live.easytutorials'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'com.amazonaws:aws-java-sdk-rds:1.11.600'
    runtimeOnly 'mysql:mysql-connector-java'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
    useJUnitPlatform()
}

6.2 application.properties

Default properties

spring.profiles.active=prod
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/rdsiamtest?useSSL=false
spring.datasource.username=root
local.database.password=root

cloud.aws.region=ap-south-1

6.3 application-prod.properties

The database endpoint URL and port number are shown in step 5.

spring.datasource.url=jdbc:mysql://easytutorials-demords.chgtv6yotna4.ap-south-1.rds.amazonaws.com:3306/rdsiamtestdb?useSSL=true
spring.datasource.username=lavkush_rdsuser

6.4 GenerateRDSAuthToken.java

package live.easytutorials.rdsiamauthenticationdemo.datasource;

import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
import com.amazonaws.services.rds.auth.GetIamAuthTokenRequest;
import com.amazonaws.services.rds.auth.RdsIamAuthTokenGenerator;

public class GenerateRDSAuthToken {

    private GenerateRDSAuthToken() {
    }

    public static String generateAuthToken(String jdbcUrl, String username, String region) {
        String hostname = getHostname(jdbcUrl);
        Integer port = getPort(jdbcUrl);

        RdsIamAuthTokenGenerator generator = RdsIamAuthTokenGenerator.builder()
                .credentials(new DefaultAWSCredentialsProviderChain())
                .region(region)
                .build();

        return generator.getAuthToken(
                GetIamAuthTokenRequest.builder()
                        .hostname(hostname)
                        .port(port)
                        .userName(username)
                        .build());
    }

    private static String getHostname(String url) {
        String hostname = url.substring(url.indexOf("://") + 3);
        hostname = hostname.substring(0, hostname.lastIndexOf(':'));
        return hostname;
    }

    private static Integer getPort(String url) {
        String port = url.substring(url.lastIndexOf(':') + 1);
        port = port.substring(0, port.lastIndexOf('/'));
        return Integer.parseInt(port);
    }
}

6.5 RdsDataSource.java

package live.easytutorials.rdsiamauthenticationdemo.datasource;

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

@Configuration
public class RdsDataSource {
    private static final long TEN_MINUTES = 10;
    private static final Logger logger = LoggerFactory.getLogger(RdsDataSource.class);

    @Value("${spring.datasource.url}")
    String jdbcUrl;
    @Value("${spring.datasource.username}")
    String username;
    @Value("${cloud.aws.region}")
    String regionName;
    @Value("${local.database.password}")
    String localDatabasePassword;

    @Bean
    public DataSource dataSource() {
        return createDataSource(jdbcUrl, username, regionName);
    }

    private HikariDataSource createDataSource(String jdbcUrl, String username, String regionName) {
        logger.info("Creating custom HikariDataSource.....");
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(jdbcUrl);
        config.setUsername(username);
        config.setPassword(generateAuthToken(jdbcUrl, username, regionName));
        HikariDataSource hikariDataSource = new HikariDataSource(config);

        // starting a scheduled thread to refresh IAM password
        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(
                new IamWorker(hikariDataSource, () -> generateAuthToken(jdbcUrl, username, regionName)),
                RdsDataSource.TEN_MINUTES,
                RdsDataSource.TEN_MINUTES,
                TimeUnit.MINUTES
        );
        
        return hikariDataSource;
    }

    private String generateAuthToken(String jdbcUrl, String username, String regionName) {
        String authToken = null;
        if (jdbcUrl.contains("localhost")) {
            authToken = localDatabasePassword;
        } else {
            authToken = GenerateRDSAuthToken.generateAuthToken(jdbcUrl, username, regionName);
        }
        System.out.println("authToken = " + authToken);
        return authToken;
    }

    private class IamWorker implements Runnable {
        private final Logger logger = LoggerFactory.getLogger(IamWorker.class);
        private HikariDataSource hikariDataSource;
        private Supplier<String> tokenGenerator;

        public IamWorker(HikariDataSource hikariDataSource, Supplier<String> tokenGenerator) {
            this.hikariDataSource = hikariDataSource;
            this.tokenGenerator = tokenGenerator;
        }

        @Override
        public void run() {
            String authToken = tokenGenerator.get();
            logger.info("Refreshing IAM credentials...");
            System.out.println("New Token :" + authToken);
            hikariDataSource.getHikariConfigMXBean().setPassword(authToken);
        }
    }
}

Output Screen

RDS IAM Authentication log

Thanks for reading this article, I hope RDS IAM Authentication is now easy for you.

Here is the Github link

Popular posts from this blog

How to upload files in Amazon S3 Bucket using Spring Boot

As stated in the title, we are going to demonstrate that how we can upload and retrieve files from the amazon s3 bucket in spring boot. For this, we must have an account on amazon web services (AWS) . And the next thing you need to have is an IAM user that has programmatic access to the s3 bucket. Follow the steps below to create an IAM user and s3 bucket. Table of Contents 1. Steps to create an IAM user in AWS with S3 bucket full access permission Step 1.1 Login to your AWS account   Step 1.2 Set the user details Step 1.3 Set user permissions Step 1.4 Create a user group and set the access policy Step 1.5 Add user to the group Step 1.6  Set the tags (optional) Step 1.7  Review the user details and permission summary Step 1.8 Download the user credentials 2. See, how to create s3 bucket. Step 2.1 Click on the "Create bucket" button. Step 2.2 Enter the bucket name and select bucket region. Step 2.3 Set file accessibility for bucket items as public/private

What Is SSL Certificate and how it works?

Deep Dive into SSL Certificate What Is an SSL Certificate? SSL (Secure Sockets Layer) is the common name for TLS (Transport Layer Security), a security protocol that enables encrypted communications between two machines. An SSL certificate is a small data file leveraging this security protocol to serve two functions: Authentication – SSL certificates serve as credentials to authenticate the identity of a website. They are issued to a specific domain name and web server after a Certificate Authority, also known as a Certification Authority (CA), performs a strict vetting process on the organization requesting the certificate. Depending on the certificate type, it can provide information about a business or website's identity and authenticate that the website is a legitimate business. Secure data communication - When SSL is installed on a web server, it enables the padlock to appear in the web browser. It activates the HTTPS protocol and creates a secure connection between th

How to Implement Spring Security in Spring Boot

Security Example in Spring Boot Implementation of Spring Security in the Spring Boot application is the key point to learn for spring boot developers. Because Authentication and Authorization are the backbones of the whole application. Getting started with the Spring Security Series, this is the first part, in this article we are going to focus on the authentication part with minimal registration. The implementation of registration flow with email verification, customizing password encoding, and setting up password strengths and rules will be explored in another separate article for each.  This article will be the base of the spring security series, the other security features will be explained on the basis of this implementation, so be focused and let's understand. The code contains proper naming & brief comments that makes it very comprehensive. If you feel any difficulty or find any issue, please drop a comment below this post The main goal of this article is to impleme

Custom Pagination with search and filters in Spring Boot

Every spring boot application is made to manage a large set of data. Also, we need to perform a search and filter the data according to need, And also we cannot load all data in one go on a single page so we need pagination too. In this article, we are going to demonstrate custom pagination with search and filter performed through ajax call. Goal: This demonstration is performed on a set of students' data. We have written a method to generate sample data.   Table of Contents 1. Initialize the project with the following dependencies 2. Set the application properties 3. Create the Student entity 4. Enum to denote the class of student 5. Create JPA repository of entity 6. Create the search & filter command object (CO) 7. Create a data transfer object (DTO) of the Entity for returning the response 8. Create a service for implementing the business login 9. Create a controller 10. Create a utility class for date conversions 11. Create the HTML Data Table design 12.

Maven or Gradle - built tool selection in Spring Boot

  Spring Boot -Selection of built tool Gradle Gradle is an open-source build automation tool that is designed to be flexible enough to build almost any type of software, It is fully open source and similar to Maven and Ant. But Gradle has taken advantage of both Maven and Ant and also it has removed the disadvantages of Maven and Ant and created as a first-class built tool. It uses domain-specific language based on the programming language Groovy , differentiating it from Apache Maven, which uses XML for its project configuration. Gradle allows to create or customize built procedure and we can create an additional task with groovy scripts that can be executed before/after built. It also determines the order of tasks run by using a directed acyclic graph . Several developers created Gradle and first released in 2007, and in 2013, it was adopted by Google as the build system for Android projects. It was designed to support multi-project builds that are expected to

Request Mapping Annotation in Spring Boot

The @RequestMapping is a class level  (also called type level) and method level annotation, it is used to process HTTP requests with specified URL patterns. It is used in and along with both @Controller and @RestController . Table of Contents Request Mapping Annotation in Spring Boot 1. How @RequestMapping annotation it is used? 2. Optional Elements of @RequestMapping 2.1 name, value and path 2.2 headers, consumes and produces 3. Specialization of @RequestMapping 1. How @RequestMapping annotation it is used? @Controller @RequestMapping("/student") public class StudentController{ @RequestMapping("/dashboard") public String dashboard(){ return "dashboard"; } @RequestMapping("result") public String result(){ return "result"; } } We can see in above code sample "/student" , "/dashboard" and "result" passed with annotation are called request value/path present in the URL