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...


Comments

Popular posts from this blog

Spring Boot OpenAI Integration: Step-by-Step Guide

Orchestration-Based Saga Architecture and Spring Boot Microservices Implementation Guide

Spring Boot 3 + Angular 15 + Material - Full Stack CRUD Application Example