Secure, Serverless, and Seamless Spring Configuration Management in AWS
Recently we transformed our AWS based servers to AWS Fargate. One of the challenges we faced was how to provide properties in a secure way to our serverless docker instances. Our Spring/Java based servers use @Value injection for many of our properties including sensitive data like database passwords. Up to this point, we had been using simple property files read by Spring using PropertyPlaceholderConfigurer. These files can be secured within Spring pretty simply using tools like Jasypt. But without access to an EC2 instance, this would not work in AWS Fargate.
The first choice as a team that is heavily invested in spring would be dockerized instances of Spring Cloud Config. However, before we went this routes, we wanted to see what AWS service we could use to simplify things. We found the AWS Parameter Store. As the documentation indicates, AWS parameter store provides “secure, hierarchical storage for configuration data management and secrets management”. We have the ability to control access to the keys using AWS IAM Roles. We also have simple key management of the encryption using AWS KMS as well as a built in audit log of changes. Using this architecture our bootup would work as follows:
We would use the KMS key available to us through the IAM role to decrytpt the secure parameters and then add them to our ApplicationContext.
The only challenge left for us was to integrate the reading of the Parameter store into our application as an out of the box replacement for the PropertyPlaceholderConfigurer we have used to inject environment based properties until now. I will outline below the steps we did to do this.
First, we implemented our SSMClient.
This class wraps all calls we need to retrieve our parameters from the SSM Parameter Store. The implementation assumes you have a hierarchial implementation as follows:
It will then retrieve all the environment parameters and strip off the prefix. Another challenge is to circumvent the AWS limit of only getting 10 parameters at at time.
The next step was to use this class and implement our own PropertyPlaceholderConfigurer. The code is as follows:
As you see in the code, at the time this code is called (before Spring has loaded) we do not have a fully autowired SSMClient to work with so we need to do this manually. So we create our SSMClient and inject the dependencies we need, manually call our PostConstruct function. After this we have our properties to pass to the base class and make them ready to inject in all the @Value parameters we have. To use the bean we just add it to our @Configuration or to the ApplicationContext xml with no one needing to change any code anywhere to use this.