- Published on
Secure application secrets by using spring vault
- Authors
- Name
- Bhuwan Prasad Upadhyay
- @IAmVuwan
Introduction
Security and working with secrets is a concern of every developer working with databases, user credentials or API keys. Vault steps in by providing a secure storage combined with access control, revocation, key rolling and auditing. In short: Vault is a service for securely accessing and storing secrets.
In this post, I’ll walk you through to store secrets into vault for your application.
![Image](/_next/image?url=%2Fstatic%2Fimages%2Farchive%2Fsecure-application-secrets-by-using-spring-vault%2Fproblem.png&w=1080&q=75)
You’ll learn how to setup Spring Cloud Vault to store and read database credentials for your stand-alone application from vault.
Start PostgreSQL and Vault locally
You can download docker-compose.yml file and just run the following to get PostgreSQL and Vault started locally in docker containers.
docker-compose up -d
Creating the VAULT PostgreSQL credentials
Let's access vault container in your terminal.
docker exec -it example-vault /bin/sh
export VAULT_TOKEN="00000000-0000-0000-0000-000000000000"
export VAULT_ADDR="http://127.0.0.1:8200"
Once you have access to vault and postgres access, do the following to enable postgres and create a role that the application will use to connect to PostgreSQL.
- Enable secrets for postgresql
vault secrets enable postgresql
- Write postgresql connection url
vault write postgresql/config/connection \
connection_url="postgresql://user:password@example-vault-db:5432/postgres?sslmode=disable"
- Apply lease setting: vault create new username and password for database
vault write postgresql/config/lease lease=1h lease_max=24h
- Add role for user in database
vault write postgresql/roles/readonly \
sql="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';
GRANT ALL ON ALL TABLES IN SCHEMA public TO \"{{name}}\";"
- To generate new set of credentials run (Vault is now configured to create and manage credentials for Postgres!)
vault read postgresql/creds/readonly
Output
Key Value
--- -----
lease_id postgresql/creds/readonly/ihWk1Q9cC3uHgjFBYOBJZFTv
lease_duration 1h
lease_renewable true
password 4878ef62-ed3d-098e-22c7-71af6891eaa8
username token-954d49c2-9269-451d-9ce9-97e5aa887222
For more info visit vault documentation here: PostgreSQL Secret Engines
Create a Spring Boot Project
curl https://start.spring.io/starter.tgz -d dependencies=postgresql,cloud-starter-vault-config,data-jpa \
-d groupId=io.github.bhuwanupadhyay \
-d artifactId=example-vault \
-d packageName=io.github.bhuwanupadhyay.tutorial \
-d baseDir=example-vault \
-d bootVersion=2.2.2.RELEASE | tar -xzvf -
cd example-vault
Using VAULT in Spring Boot Project with Spring Cloud
Needed maven dependencies under you maven project pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-vault-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-vault-config-databases</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
The spring cloud use bootstrap.yml
or bootstrap.properties
file. So, to connect with vault you need to define following values in bootstrap.yml
file.
spring:
application:
name: example-vault
cloud:
vault:
uri: http://localhost:8200
token: '00000000-0000-0000-0000-000000000000'
database:
enabled: true
role: readonly
backend: postgresql
datasource:
url: jdbc:postgresql://localhost:5432/postgres
jpa:
hibernate:
ddl-auto: create-drop
Database Access: PostgreSQL (read/write)
Let's implement customer entity and its spring data repository as follows:
Entity
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
public Customer() {
}
public Customer(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Customer customer = (Customer) o;
return Objects.equals(id, customer.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
Entity Data Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
}
Test Scenario
@Spring BootTest
class DemoApplicationTests {
@Autowired
private CustomerRepository repository;
@BeforeEach
void setUp() {
repository.deleteAll();
}
@Test
void canAddCustomer() {
String name = "Vault - " + UUID.randomUUID().toString();
repository.save(new Customer(name));
assertEquals(name, repository.findAll().get(0).getName());
}
}
You can find example on github: Source Code