Vaulting into Security: A Guide to Spring Cloud Vault

Amit Chavda
Simform Engineering
8 min readMay 20, 2024

Combine Spring Boot and HashiCorp Vault to manage sensitive data.

Spring Cloud Vault

In this blog, we’ll explore how Spring Boot seamlessly integrates with HashiCorp Vault using Spring Cloud Vault. Discover how this integration simplifies secure data management and access, ensuring strong security and smooth secret retrieval processes.

Let’s start by understanding the terms:

What are Spring Cloud Vault and HashiCorp Vault?

Spring Cloud Vault extends the popular Spring Framework seamlessly integrating with HashiCorp Vault. It’s designed to manage secrets and sensitive configuration in Spring-based applications, simplifying the process of accessing and using secrets stored in Vault. This allows you to focus on building features without the headache of managing credentials and other sensitive information.

HashiCorp Vault is designed to manage secrets and protect sensitive data in a modern infrastructure environment. It provides a secure, centralized platform for storing, accessing, and distributing secrets like passwords, API keys, certificates, and encryption keys. Vault addresses challenges such as managing secrets in distributed systems, including security issues, compliance, and scalability.

Key Terms in HashiCorp Vault

Secret: A secret is anything Vault returns containing confidential or cryptographic material. Dynamic secrets always have an associated lease, and static secrets do not. This means clients cannot assume that the dynamic secret contents can be used indefinitely.

Secrets engine: A secrets engine is responsible for managing secrets. Simple secret engines, such as the “kv” secrets engine, return the same secret when queried, while others generate unique secrets dynamically each time they’re queried. This allows unique secrets to be used, allowing Vault to do fine-grained revocation and policy updates.

Server: Vault depends on a long-running instance operating as a server. The Vault server provides an API for clients to interact with and manages the interaction between all the secret engines, ACL enforcement, and secret lease revocation. Server-based architecture decouples clients from the security keys and policies, enables centralized audit logging, and simplifies administration for operators.

Storage backend: A storage backend is responsible for storing durable encrypted data. Backends are only expected to provide durability. The storage backend is configured when starting the Vault server.

Key Features of HashiCorp Vault

  1. Secrets Management: Vault provides a secure storage mechanism for secrets, allowing organizations to store sensitive data in an encrypted format. It supports dynamic secret generation and revocation, enabling applications to request short-lived credentials on demand.
  2. Dynamic Secrets: Vault can generate dynamic secrets on the fly, which are short-lived and automatically revoked after a certain period or when no longer needed. This approach enhances security by minimizing the exposure of credentials and reducing the risk of unauthorized access.
  3. Encryption as a Service: Vault offers encryption services for data-at-rest and data-in-transit, allowing organizations to encrypt sensitive data before storing it in Vault or transmitting it over the network. This helps protect data from unauthorized access and ensures compliance with security standards.
  4. Access Control Policies: Vault provides fine-grained access control policies to regulate who can access which secrets and under what conditions. Administrators can define policies based on roles, permissions, and authentication methods, ensuring that only authorized users and applications can access sensitive data.
  5. Audit Logging: Vault logs all access and operations performed within the system, providing a comprehensive audit trail for compliance purposes. Administrators can monitor and review activity logs to track changes, identify security incidents, and ensure accountability.
  6. Integration with Identity Providers: Vault supports integration with various identity providers and authentication methods, including LDAP, Active Directory, OAuth, and multi-factor authentication (MFA). This allows organizations to leverage existing identity management systems and enforce strong authentication mechanisms.

Setting Up HashiCorp Vault

Here’s a step-by-step guide to download, install, and set up HashiCorp Vault on your system, as well as integrating it into your Spring Boot application environment:

Download and Install HashiCorp Vault

1. Visit the HashiCorp website: Head over to HashiCorp Vault Install and choose the version compatible with your operating system. HashiCorp offers downloads for Windows, macOS, and Linux. Since we’re using Ubuntu, we’ll download the Linux version

2. Download the binary: Click the download link to download the Vault binary file.

3. Extract the binary: The downloaded file will look like this vault_1.16.2_linux_amd64.zip Now extract it to some directory or run the following command in Ubuntu to unzip it.

unzip vault_1.16.2_linux_amd64.zip

4. Move the executable: Move the extracted vault binary to a directory in your system path, such as /usr/local/bin/, so you can run it from anywhere:

sudo mv vault /usr/local/bin/

5. Verify installation: Check that Vault is installed correctly by running:

vault --version
Vault Version

Configure and Start HashiCorp Vault

1. Initialize vault: Open a terminal and navigate to the directory where Vault is installed. Run the vault operator init command to initialize Vault. This generates an initial root token and unseal keys.

2. Set the vault address: Set the environment variable VAULT_ADDR to the address of your Vault server.

export VAULT_ADDR=http://localhost:8200

3. Start the vault server: After installation, start the Vault server in development mode for testing.

Run the command: vault server -dev . After starting the server successfully, you’ll see the following warning log, where you’ll find the root token to access the server.

Root Token

4. Create secrets: After starting the server, create secrets using the commands provided. For instance, you can create a database secret and generate Key-Value (KV) secrets to access the Spring Boot application.

Create KV Key-value) Secrets
Create database Secrets

The files shown in this step are available with the project in Github at the root: Github Link

Create a Spring Boot App

  1. Create a new Spring Boot project: Go to Spring Initializr and configure your project with the necessary dependencies, including Spring Cloud Vault Config:
<!-- Spring Cloud Vault Config -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-vault-config</artifactId>
<version>4.1.1</version>
</dependency>

<!-- Spring Cloud Vault Config Databases -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-vault-config-databases</artifactId>
<version>4.1.1</version>
</dependency>

2. Add application properties for Vault: Add the necessary configuration properties to your application.properties or application.yml file to connect to your Vault instance. For example:

spring:
config:
import: vault://
application:
name: spring-cloud-vault-demo
profiles:
active: local
cloud:
vault:
authentication: TOKEN
token: #token
scheme: http
host: localhost
port: 8200
fail-fast: true
kv:
enabled: true
backend: secret
default-context: spring-cloud-vault-demo
database:
enabled: true
role: dynamic-role

spring.config.import: Specifies the location of additional configuration sources to import. In this case, it imports configurations from Vault using the vault:// prefix.

spring.cloud.vault.authentication: Specifies the method of authentication to be used with Vault. In this example, the “TOKEN” authentication method is used.

spring.cloud.vault.token: Specifies the token used for authentication with Vault.

spring.cloud.vault.fail-fast: Specifies whether the application should fail immediately if it cannot connect to Vault during startup. If set to true, the application will fail fast; if false, it will continue startup even if Vault is unavailable.

spring.cloud.vault.kv.enabled: Specifies whether the Key-Value (KV) secret backend is enabled. If set to true, it enables the KV secret backend.

spring.cloud.vault.kv.backend: Specifies the backend mount path for the KV secrets engine. In this example, it is set to “secret”.

spring.cloud.vault.database.enabled: Specifies whether the database secret backend is enabled. If set to true, it enables the database secret backend.

spring.cloud.vault.database.role: Specifies the role used for dynamic database credentials. In this case, it uses a dynamic role to generate database credentials.

3. Add database properties: We have not provided a database username and password. The vault will inject the credentials automatically.

spring:
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/spring-cloud-vault-demo
# username and password properties will be injected by the vault
jpa:
#show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
hibernate:
ddl-auto: update

4. Create user entity: Define the structure of the User object, including attributes like username and email.

@Data
@Entity
@Table(name = "users")
@AllArgsConstructor
@NoArgsConstructor
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private long id;

private String username;
private String password;
}

5. Create repository: Implement a repository interface that extends JpaRepository<User, Long> to perform CRUD operations on the User entity.

@Repository
public interface UserRepository extends JpaRepository<User, Long> {}

6. Create service: Add methods to add a new user and return all users from the database.

@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;

public UserDto add(UserDto userDto) {
User saved = userRepository.save(new User(0, userDto.getUsername(), userDto.getPassword()));
userDto.setId(saved.getId());
return userDto;
}

public List<UserDto> findAll() {
return userRepository.findAll().parallelStream().map(user -> new UserDto(user.getId(), user.getUsername(), user.getPassword())).toList();
}
}

7. Create DTO to access KV secrets:

@Data
@Component
@ConfigurationProperties("client-credentials")
public class ClientCredentials {

private String clientId;
private String clientSecret;
}

8. Enable configuration properties: Add @EnableConfigurationProperties(ClientCredentials.class) annotation in SpringCloudVaultDemoApplication to enable vault configuration.

9. Create a controller: Enable the users to interact with the application.

@RestController
@RequiredArgsConstructor
public class AppController {

private final UserService userService;
private final ClientCredentials clientCredentials;

@GetMapping("/user")
public ResponseEntity<GenericResponse> getUsers() {
return ResponseEntity.ok(new GenericResponse(true, "Users returned successfully", userService.findAll(), HttpStatus.OK.value(), LocalDateTime.now()));
}

@PostMapping("/user")
public ResponseEntity<GenericResponse> addUser(@RequestBody UserDto userDto) {
return ResponseEntity.ok(new GenericResponse(true, "User saved successfully", userService.add(userDto), HttpStatus.CREATED.value(), LocalDateTime.now()));
}


@GetMapping("/clientCredentials")
public ResponseEntity<GenericResponse> getClientCredentials() {
return ResponseEntity.ok(new GenericResponse(true, "Users returned successfully", clientCredentials, HttpStatus.OK.value(), LocalDateTime.now()));
}
}

When you hit the http://localhost:8085/clientCredentials endpoint, you’ll see the response with the credentials that we have stored in the vault.

{
"success": true,
"message": "Users returned successfully",
"data": {
"clientId": "269d98e4922fb3895e9ae2108cbb5064",
"clientSecret": "ZYDPLLBWSK3MVQJSIYHB1OR2JXCY0X2C5UJ2QAR2MAAIT5Q"
},
"code": 200,
"timestamp": "2024-05-13T13:24:51.724979062"
}

If your application is running and you can add a new user using the API, it means the database credentials are successfully injected by Vault.

Conclusion

Integrating HashiCorp Vault with Spring Boot is a robust solution for enhancing security, streamlining configuration management, and ensuring compliance within your application infrastructure.

By leveraging Vault’s dynamic secrets management, centralized configuration storage, and precise access control, your Spring Boot applications can significantly boost their security posture while maintaining agility and scalability.

For more updates on the latest tools and technologies, follow the Simform Engineering blog.

Follow Us: Twitter | LinkedIn

--

--

Amit Chavda
Simform Engineering

Java | Spring Boot | AWS | Docker | Jenkins | PostgreSQL