Table of Contents
Introduction
In modern application development, handling sensitive information like database credentials securely is of utmost importance. Hardcoding these credentials in your application code or configuration files is a serious security risk, as they can be easily exposed or accidentally committed to version control systems.
AWS Secrets Manager provides a secure and scalable solution for managing secrets, including database credentials, API keys, and other sensitive data.
In this blog post, we’ll explore how to integrate AWS Secrets Manager with a Spring Boot application to securely retrieve and use database credentials. We’ll also discuss best practices for handling AWS credentials in a production environment.
How To Create AWS Secret Manager in AWS
Before we dive into the code, let’s set up AWS Secrets Manager and create a secret for our database credentials.
- Create an AWS Account: If you haven’t already, create an AWS account and log in to the AWS Management Console.
- Navigate to AWS Secrets Manager: In the AWS Management Console, search for the Secrets Manager service and navigate to it.
- Create a New Secret: Click on “Store a new secret” and select the “Credential pair” option. Provide a name for your secret (e.g., “my-db-credential”) and enter your database username and password in the respective fields.
- Copy the Secret ARN: After creating the secret, copy the Amazon Resource Name (ARN) of the secret, as you’ll need it later in your application code.
Integrating AWS Secrets Manager with Spring Boot
Now that we have our database credentials stored securely in AWS Secrets Manager, let’s integrate it with our Spring Boot application.
- Add AWS SDK Dependencies: In your Spring Boot application, add the required AWS SDK dependencies to your project’s build file (e.g.,
pom.xml
for Maven orbuild.gradle
for Gradle).
<dependencies>
<!-- Spring Boot dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- AWS SDK for Java -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-secretsmanager</artifactId>
<version>1.12.300</version>
</dependency>
<!-- Gson for JSON parsing -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.1</version>
</dependency>
<!-- Lombok for boilerplate code reduction -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
- Create a Configuration Class: Create a new class (e.g.,
ApplicationConfig.java
) and annotate it with@Configuration
.
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.secretsmanager.AWSSecretsManager;
import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder;
import com.amazonaws.services.secretsmanager.model.*;
import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class ApplicationConfig {
// ...
}
- Retrieve AWS Credentials: In the
ApplicationConfig
class, inject the AWS credentials using@Value
annotations. These credentials can be provided as environment variables or read from a credentials file.
@Value("${cloud.aws.credentials.access-key}")
private String accessKey;
@Value("${cloud.aws.credentials.secret-key}")
private String secretkey;
- Create a DataSource Bean: Define a
@Bean
method to create aDataSource
object using the database credentials retrieved from AWS Secrets Manager.
@Bean
public DataSource dataSource() {
AwsSecrets secrets = getSecret();
return DataSourceBuilder
.create()
.url("jdbc:" + secrets.getEngine() + "://" + secrets.getHost() + ":" + secrets.getPort() + "/mydb")
.username(secrets.getUsername())
.password(secrets.getPassword())
.build();
}
- Retrieve Secrets from AWS Secrets Manager: Create a private method
getSecret()
to retrieve the database credentials from AWS Secrets Manager using the AWS SDK for Java.
private AwsSecrets getSecret() {
String secretName = "mydb-db-credential";
String region = "us-east-2";
AWSSecretsManager client = AWSSecretsManagerClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretkey)))
.build();
GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest()
.withSecretId(secretName);
GetSecretValueResult getSecretValueResult = null;
try {
getSecretValueResult = client.getSecretValue(getSecretValueRequest);
} catch (Exception e) {
throw e;
}
if (getSecretValueResult.getSecretString() != null) {
String secret = getSecretValueResult.getSecretString();
return new Gson().fromJson(secret, AwsSecrets.class);
}
return null;
}
In this method, we’re using the AWSSecretsManager
client to retrieve the secret value from AWS Secrets Manager. We’re also using the Gson
library to deserialize the secret string into an AwsSecrets
object.
Final Class of ApplicationConfig.java
package com.example.awssecretsmanager;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.secretsmanager.AWSSecretsManager;
import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder;
import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest;
import com.amazonaws.services.secretsmanager.model.GetSecretValueResult;
import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class ApplicationConfig {
@Value("${cloud.aws.credentials.access-key}")
private String accessKey;
@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;
private Gson gson = new Gson();
@Bean
public DataSource dataSource() {
AwsSecrets secrets = getSecret();
return DataSourceBuilder
.create()
.url("jdbc:" + secrets.getEngine() + "://" + secrets.getHost() + ":" + secrets.getPort() + "/mydb")
.username(secrets.getUsername())
.password(secrets.getPassword())
.build();
}
private AwsSecrets getSecret() {
String secretName = "mydb-db-credential";
String region = "us-east-2";
AWSSecretsManager client = AWSSecretsManagerClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey)))
.build();
GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest()
.withSecretId(secretName);
GetSecretValueResult getSecretValueResult = null;
try {
getSecretValueResult = client.getSecretValue(getSecretValueRequest);
} catch (Exception e) {
throw e;
}
if (getSecretValueResult.getSecretString() != null) {
String secret = getSecretValueResult.getSecretString();
return gson.fromJson(secret, AwsSecrets.class);
}
return null;
}
}
- Create an AwsSecrets POJO: Create a simple POJO (Plain Old Java Object) to hold the database credentials retrieved from AWS Secrets Manager.
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AwsSecrets {
private String username;
private String password;
private String host;
private String engine;
private String port;
private String dbInstanceIdentifier;
}
- Configure Application Properties: In your
application.properties
orapplication.yml
file, provide the necessary configuration for your Spring Boot application, including the AWS credentials.
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
server.port=5000
spring.jpa.properties.max_allowed_packet=2000
cloud:
aws:
region: static
auto: false
credentials:
access-key: YOUR_AWS_ACCESS_KEY
secret-key: YOUR_AWS_SECRET_KEY
Replace YOUR_AWS_ACCESS_KEY
and YOUR_AWS_SECRET_KEY
with your actual AWS credentials.
Handling AWS Credentials in a Production Environment
While we’ve used hardcoded AWS credentials in the example for simplicity, it’s essential to handle these credentials securely in a production environment. Here are some best practices:
- Use AWS Identity and Access Management (IAM) Roles: Instead of hardcoding AWS credentials in your application, you can assign an IAM role with the necessary permissions to your application’s compute resource (e.g., EC2 instance, ECS task, or Lambda function). The AWS SDK will automatically retrieve the credentials from the assigned IAM role.
- Use AWS Secrets Manager for AWS Credentials: If you can’t use IAM roles, you can store your AWS credentials as a secret in AWS Secrets Manager and retrieve them programmatically in your application.
- Limit Access to AWS Credentials: Ensure that your AWS credentials have the minimum required permissions to access the necessary resources. Follow the principle of least privilege when assigning permissions.
- Rotate AWS Credentials Regularly: Rotate your AWS credentials periodically to mitigate the risk of credential exposure or misuse.
- Use Environment Variables or Credentials File: If you need to provide AWS credentials directly to your application, use environment variables or a credentials file instead of hardcoding them in your application code or configuration files.
Conclusion
In this blog post, we’ve explored how to integrate AWS Secrets Manager with a Spring Boot application to securely retrieve and use database credentials. We’ve also discussed best practices for handling AWS credentials in a production environment.
By leveraging AWS Secrets Manager, you can centralize the management of sensitive data, rotate secrets automatically, and improve your application’s security posture by eliminating hardcoded credentials. The provided code examples demonstrate how to retrieve database credentials from AWS Secrets Manager and create a DataSource
.
Ref: https://github.com/Java-Techie-jt/springboot-aws-secrets-manager