In my first post about encryption I discussed a solution that was basic. The approach used Spring’s Encryptor classes, one of which, the queryable TextEncryptor, was deprecated due to it being insecure. Since then, I have spent time enhancing other aspects of the platform, as well as kicking off an app based on it. To feel secure deploying this app to a production environment, I felt that I needed to revisit my encryption system and practices. So I spent two weeks of whatever hours I could eke before bedtime to research Vault.
HashiCorp’s Vault is well known and has features needed in production systems, such as secrets management, data encryption, and identity-based access. The first two features are where I am going to focus on in this post.
While Vault has a dev mode, I suggest against it. It is important to set up our dev environment as close to production as possible. We will learn much more about Vault’s intricacies, as well as be better prepared when we deploy to production. Yes, this will be more involved, Vault is a complex product, and our time will be well spent. So, let us first create a non-prod instance of HashiCorp’s Vault.
After we download Vault, we need to create a configuration file. This configuration tells Vault what port it will listen on, its physical storage, etc. Follow the deployment tutorial, https://learn.hashicorp.com/tutorials/vault/getting-started-deploy, all the way to “Seal/Unseal”. At this point, we should have a vault instance running that is unsealed and ready to be used. Make sure you save the unseal and login key!
Next, we will use our Vault for encrypting data in our app. HashiCorp, again has a great tutorial on how to set this up https://learn.hashicorp.com/tutorials/vault/eaas-transit.
At this point we should have a Vault instance running, unsealed, with a policy configured that will allow us to access our encryption and decryption endpoints. We will now move on to our SpringBoot app.
Let’s update our pom.xml and add a couple of artifacts that will help us as our app grows.
<properties> <spring-cloud.version>2020.0.3</spring-cloud.version> </properties <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-vault-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-vault-config-databases</artifactId> </dependency> </dependencies>
The first dependency, spring-cloud-starter-vault-config, is what is going to allow us to read secrets, as well as encrypt/decrypt data using Vault. The spring-cloud-vault-config-database will allows us to create dynamic database credentials (we’ll talk about this in more depth in another post). So, lets move forward and create a bootstrap.yml file that will allow our app connect to Vault.
spring: application: name: <your SpringBoot artifact id> cloud: vault: # In non-prod we're running vault locally. host: localhost port: 8200 # Only used in non-prod where we aren't using # a cert. scheme: http authentication: token
Now, when we startup our app, we will be able to see our secrets injected from Vault. For example, if you stored the secret
vault kv put secret/app-name super.secret.value=shhh
You’ll see the value shhh show up in the member variable, secret.
@SpringBootApplication public class TestApp { @Vault("${super.secret.value}") private String secret; ... }
I know this isn’t a full runnable demo, and if you search you’ll find a number of options if that’s what you’re looking for. Do be aware, there are other solutions that require more code and configurations. Even https://spring.io/guides/gs/vault-config/ uses a more complex @ConfigurationProperties approach which I currently don’t see the need for. My suggestion is to always keep our application code as simple as can be. Using straight forward @Value injection, like that mentioned above, is simple and clean. While I want to post another blog’s example with a straight forward demo such as this, I am not easily finding one. If you’re having trouble, reach out to me.
If you found this helpful, and you happen to be looking for hosting, please consider Dreamhost. If you’d like to sign up with them and feel inclined to throw me some credit, please use my referral link https://www.dreamhost.com/r.cgi?571777.