AWS Prescriptive Guidance
Patterns

Manage credentials using AWS Secrets Manager

R Type :ReArchitect

source :Security

target :AWS Secrets Manager

tags :aws secrets manager, keys and credentials, databases, java spring

categories :Software Infrastructure

Summary

This pattern walks you through using AWS Secrets Manager to dynamically fetch database credentials for a Java Spring application.

Assumptions and Prerequisites

Prerequisites

  • An AWS account with access to Secrets Manager

  • A Java Spring application

Architecture

Source technology stack

  • A Java Spring application with code that accesses a database, with DB credentials managed from the application.properties file.

Target technology stack

  • A Java Spring application with code that accesses a database, with DB credentials managed in Secrets Manager. The application.properties file holds the secrets to Secrets Manager.

Secrets Manager integration with an application

Tools Used

Secrets Manager - AWS Secrets Manager is an AWS service that makes it easier for you to manage secrets. Secrets can be database credentials, passwords, third-party API keys, and even arbitrary text. You can store and control access to these secrets centrally by using the Secrets Manager console, the Secrets Manager command-line interface (CLI), or the Secrets Manager API and SDKs.

In the past, when you created a custom application that retrieves information from a database, you typically had to embed the credentials (the secret) for accessing the database directly in the application. When it was time to rotate the credentials, you had to do much more than create new credentials. You had to invest time to update the application to use the new credentials, and then distribute the updated application. If you had multiple applications that shared credentials and you missed updating one of them, the application would break. Because of this risk, many users chose not to regularly rotate their credentials, which effectively substituted one risk for another.

Secrets Manager enables you to replace hard-coded credentials in your code (including passwords), with an API call to retrieve the secret programmatically. This helps ensure that the secret can't be compromised by someone examining your code, because the secret simply isn't there. You can also configure Secrets Manager to automatically rotate the secret for you according to a schedule that you specify. This enables you to replace long-term secrets with short-term ones, which helps to significantly reduce the risk of compromise.

For more information, see the https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.htmlAWS Secrets Manager documentation.

Epics

Store secret in Secrets Manager

Tasks

Title Description Skills Predecessor
Store the DB credentials as a secret in Secrets Manager. Store Amazon Relational Database Service (Amazon RDS) or other DB credentials as a secret in Secrets Manager by following the steps in https://docs.aws.amazon.com/secretsmanager/latest/userguide/manage_create-basic-secret.html. Sys Admin
Set permissions for the Spring application to access Secrets Manager. Set the appropriate permissions based on the way Secrets Manager is invoked. Follow these steps: https://docs.aws.amazon.com/secretsmanager/latest/userguide/manage_retrieve-secret.html. If the application is hosted on an EC2 instance, here's an example policy for Amazon EC2 to access Secrets Manager: { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::<aws accnt id>:role/EC2RoleToAccessSecrets", "Action": "secretsmanager:GetSecretValue", "Resource": "*" "Condition": { "ForAnyValue:StringEquals": { "secretsmanager:VersionStage": "AWSCURRENT" } } }] } Sys Admin

Update the Spring application

Tasks

Title Description Skills Predecessor
Add JAR dependencies to use Secrets Manager. Maven: <groupId>com.amazonaws</groupId> <artifactId>aws-java-sdk-secretsmanager</artifactId> <version>1.11. 355 </version> Gradle: compile group: 'com.amazonaws', name: 'aws-java-sdk-secretsmanager', version: '1.11.355' Java developer
Add the details of the secret to the Spring application. Update the application.properties file with the secret name, endpoints, and AWS Region. For example: spring.aws.secretsmanager.secretName=postgres-local spring.aws.secretsmanager.endpoint=secretsmanager.us-east-1.amazonaws.com spring.aws.secretsmanager.region=us-east-1 Java developer
Update the DB credentials retrieval code in Java. In the application, update the Java code that fetches the DB credentials to fetch those details from Secrets Manager. For example: String secretName = env.getProperty("spring.aws.secretsmanager.secretName"); String endpoints = env.getProperty("spring.aws.secretsmanager.endpoint"); String AWS Region = env.getProperty("spring.aws.secretsmanager.region"); AwsClientBuilder.EndpointConfiguration config = new AwsClientBuilder.EndpointConfiguration(endpoints, AWS Region); AWSSecretsManagerClientBuilder clientBuilder = AWSSecretsManagerClientBuilder.standard(); clientBuilder.setEndpointConfiguration(config); AWSSecretsManager client = clientBuilder.build(); ObjectMapper objectMapper = new ObjectMapper(); JsonNode secretsJson = null; ByteBuffer binarySecretData; GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest().withSecretId(secretName); GetSecretValueResult getSecretValueResponse = null; try { getSecretValueResponse = client.getSecretValue(getSecretValueRequest); } catch (ResourceNotFoundException e) { log.error("The requested secret " + secretName + " was not found"); } catch (InvalidRequestException e) { log.error("The request was invalid due to: " + e.getMessage()); } catch (InvalidParameterException e) { log.error("The request had invalid params: " + e.getMessage()); } if (getSecretValueResponse == null) { return null; } // Decrypted secret using the associated KMS CMK // Depending on whether the secret was a string or binary, one of these fields will be populated String secret = getSecretValueResponse.getSecretString(); if (secret != null) { try { secretsJson = objectMapper.readTree(secret); } catch (IOException e) { log.error("Exception while retreiving secret values: " + e.getMessage()); } } else { log.error("The Secret String returned is null"); return null; } String host = secretsJson.get("host").textValue(); String port = secretsJson.get("port").textValue(); String dbname = secretsJson.get("dbname").textValue(); String username = secretsJson.get("username").textValue(); String password = secretsJson.get("password").textValue(); } Java developer

References and Help

References

Contact and help

Migration Pattern Library Support: aws-mpl@amazon.com