AWS Secret Manager Service as application properties with Spring boot

Secrets Manager enables us to supersede hardcoded credentials in our code, including passwords, with an API call to Secrets Manager to retrieve the secret programmatically. These avails ascertain the secret can't be compromised by someone examining our code, because the secret no longer subsists in the code. Withal, we can configure Secrets Manager to automatically rotate the secret for us according to a designated schedule. This enables us to supersede long-term secrets with short-term ones, significantly abbreviating the peril of compromise.

Overview


1. The admin creates a new secret in AWS Secrets Manager
2. A Spring Boot application uses the secret name to access the secrets stored in AWS Secrets Manager

Step 1: Create & Store secrets in AWS Secret Manager.

Use the AWS Console to create and store a new secret in AWS Secrets Manager. Link

Step 2: Add the below dependency to the pom.xml file.

<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-secretsmanager</artifactId>
<version>1.11.942</version>
</dependency>

Programmatic approach

Step 3: Create an application listener class to retrieve secrets

package com.knf.aws.secretmanager.demo.listner;

import java.io.IOException;
import java.util.Properties;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import com.amazonaws.services.secretsmanager.AWSSecretsManager;
import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder;
import com.amazonaws.services.secretsmanager.model.DecryptionFailureException;
import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest;
import com.amazonaws.services.secretsmanager.model.GetSecretValueResult;
import com.amazonaws.services.secretsmanager.model.InternalServiceErrorException;
import com.amazonaws.services.secretsmanager.model.InvalidParameterException;
import com.amazonaws.services.secretsmanager.model.InvalidRequestException;
import com.amazonaws.services.secretsmanager.model.ResourceNotFoundException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class PropertiesListener implements
ApplicationListener<ApplicationPreparedEvent> {

private ObjectMapper mapper = new ObjectMapper();
private static final String AWS_SECRET_NAME = "<AWS_SECRET_NAME>";
private static final String AWS_REGION = "<AWS_REGION>";
private static final String USERNAME = "username";
private static final String PASSWORD = "password";

@Override
public void onApplicationEvent(ApplicationPreparedEvent event) {
// Get username and password from AWS Secret Manager using secret name
String secretJson = getSecret();
String database = getString(secretJson, USERNAME);
String uri = getString(secretJson, PASSWORD);
ConfigurableEnvironment environment = event.
getApplicationContext().getEnvironment();
Properties props = new Properties();
props.put(USERNAME, database);
props.put(PASSWORD, uri);
environment.getPropertySources().addFirst
(new PropertiesPropertySource("aws.secret.manager", props));
}

private String getSecret() {
// Create a Secrets Manager client
AWSSecretsManager client = AWSSecretsManagerClientBuilder.
standard().withRegion(AWS_REGION).build();
String secret = null;
GetSecretValueRequest getSecretValueRequest = new
GetSecretValueRequest().withSecretId(AWS_SECRET_NAME);
GetSecretValueResult getSecretValueResult = null;
try {
getSecretValueResult = client.getSecretValue(getSecretValueRequest);
if (getSecretValueResult != null && getSecretValueResult.
getSecretString() != null) {
secret = getSecretValueResult.getSecretString();
}
} catch (DecryptionFailureException | InternalServiceErrorException
| InvalidParameterException
| InvalidRequestException | ResourceNotFoundException e) {

return null;
}
return secret;
}

private String getString(String json, String path) {
try {
JsonNode root = mapper.readTree(json);
return root.path(path).asText();
} catch (IOException e) {

return null;
}
}
}


Step 4: Add the new application listener to the spring.factories

We will also need to add the new application listener to the spring.factories file in the folder src/main/resources/META-INF/spring.factories:
org.springframework.context.ApplicationListener=com.knf.aws.secretmanager.demo.listner.PropertiesListener

More...


Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Spring Boot + Mockito simple application with 100% code coverage

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete