This guide walks you through the process of using Spring Vault to build an application that loads secrets from HashiCorp Vault, a secrets management tool.

What you’ll build

You will load secrets stored in Vault and use the transit encryption backend.

What you’ll need

How to complete this guide

Like most Spring Getting Started guides, you can start from scratch and complete each step or you can bypass basic setup steps that are already familiar to you. Either way, you end up with working code.

To start from scratch, move on to Build with Gradle.

To skip the basics, do the following:

When you finish, you can check your results against the code in gs-accessing-vault/complete.

Build with Gradle

Build with Gradle

First you set up a basic build script. You can use any build system you like when building apps with Spring, but the code you need to work with Gradle and Maven is included here. If you’re not familiar with either, refer to Building Java Projects with Gradle or Building Java Projects with Maven.

Create the directory structure

In a project directory of your choosing, create the following subdirectory structure; for example, with mkdir -p src/main/java/hello on *nix systems:

└── src
    └── main
        └── java
            └── hello

Create a Gradle build file

build.gradle

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:2.2.1.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

bootJar {
    baseName = 'gs-accessing-vault'
    version =  '0.1.0'
}

repositories {
    mavenCentral()
}

ext {
    springCloudVersion = 'Greenwich.SR2'
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
    compile('org.springframework.cloud:spring-cloud-starter-vault-config')
    testCompile("org.springframework.boot:spring-boot-starter-test")
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

The Spring Boot gradle plugin provides many convenient features:

  • It collects all the jars on the classpath and builds a single, runnable "über-jar", which makes it more convenient to execute and transport your service.

  • It searches for the public static void main() method to flag as a runnable class.

  • It provides a built-in dependency resolver that sets the version number to match Spring Boot dependencies. You can override any version you wish, but it will default to Boot’s chosen set of versions.

Build with Maven

Build with Maven

First you set up a basic build script. You can use any build system you like when building apps with Spring, but the code you need to work with Maven is included here. If you’re not familiar with Maven, refer to Building Java Projects with Maven.

Create the directory structure

In a project directory of your choosing, create the following subdirectory structure; for example, with mkdir -p src/main/java/hello on *nix systems:

└── src
    └── main
        └── java
            └── hello

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.springframework</groupId>
    <artifactId>gs-accessing-vault</artifactId>
    <version>0.1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
    </parent>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR2</spring-cloud.version>
    </properties>

    <dependencies>

        <!-- Vault Starter -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-vault-config</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

The Spring Boot Maven plugin provides many convenient features:

  • It collects all the jars on the classpath and builds a single, runnable "über-jar", which makes it more convenient to execute and transport your service.

  • It searches for the public static void main() method to flag as a runnable class.

  • It provides a built-in dependency resolver that sets the version number to match Spring Boot dependencies. You can override any version you wish, but it will default to Boot’s chosen set of versions.

Build with your IDE

Build with your IDE

Install and launch HashiCorp Vault

With your project set up, you can install and launch HashiCorp Vault.

If you are using a Mac with homebrew, this is as simple as:

$ brew install vault

Alternatively, download Vault for your operating system from https://www.vaultproject.io/downloads.html:

$ https://releases.hashicorp.com/vault/1.2.1/vault_1.2.1_darwin_amd64.zip
$ unzip vault_1.2.1_darwin_amd64.zip

For other systems with package management, such as Redhat, Ubuntu, Debian, CentOS, and Windows, see instructions at https://www.vaultproject.io/docs/install/index.html.

After you install Vault, launch it in a console window. This command also starts up a server process.

$ vault server --dev --dev-root-token-id="00000000-0000-0000-0000-000000000000"

You should see the following as one of the last output lines:

[INFO ] core: post-unseal setup complete
The command above starts Vault in development mode using in-memory storage without transport encryption. This is fine for evaluating Vault locally. Make sure to use proper SSL certificates and a reliable storage backend for production use. Consult Vault’s Production Hardening guide for further details.

Store secrets in Vault

Vault is a secrets management system allowing you to store sensitive data which is encrypted at rest. It’s ideal to store sensitive configuration details such as passwords, encryption keys, API keys.

Launch another console window to store application configuration in Vault using the Vault command line.

First, you need to set two environment variables to point the Vault CLI to the Vault endpoint and provide an authentication token.

$ export export VAULT_TOKEN="00000000-0000-0000-0000-000000000000"
$ export VAULT_ADDR="http://127.0.0.1:8200"

Now you can store a configuration key-value pairs inside Vault:

$ vault kv put secret/github github.oauth2.key=foobar

Configure your application

Here you configure your application with bootstrap.properties. Spring Cloud Vault is configured with the bootstrap context.

src/main/resources/bootstrap.properties

spring.cloud.vault.token=00000000-0000-0000-0000-000000000000
spring.cloud.vault.scheme=http

Create an Application class

Here you create an Application class with all the components.

src/main/java/hello/Application.java

package hello;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.vault.core.VaultKeyValueOperationsSupport.KeyValueBackend;
import org.springframework.vault.core.VaultSysOperations;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.core.VaultTransitOperations;
import org.springframework.vault.support.VaultMount;
import org.springframework.vault.support.VaultResponse;

@SpringBootApplication
public class Application implements CommandLineRunner {

  @Autowired
  private VaultTemplate vaultTemplate;

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

  @Override
  public void run(String... strings) throws Exception {

    // You usually would not print a secret to stdout
    VaultResponse response = vaultTemplate
        .opsForKeyValue("secret", KeyValueBackend.KV_2).get("github");
    System.out.println("Value of github.oauth2.key");
    System.out.println("-------------------------------");
    System.out.println(response.getData().get("github.oauth2.key"));
    System.out.println("-------------------------------");
    System.out.println();

    // Let's encrypt some data using the Transit backend.
    VaultTransitOperations transitOperations = vaultTemplate.opsForTransit();

    // We need to setup transit first (assuming you didn't set up it yet).
    VaultSysOperations sysOperations = vaultTemplate.opsForSys();

    if (!sysOperations.getMounts().containsKey("transit/")) {

      sysOperations.mount("transit", VaultMount.create("transit"));

      transitOperations.createKey("foo-key");
    }

    // Encrypt a plain-text value
    String ciphertext = transitOperations.encrypt("foo-key", "Secure message");

    System.out.println("Encrypted value");
    System.out.println("-------------------------------");
    System.out.println(ciphertext);
    System.out.println("-------------------------------");
    System.out.println();

    // Decrypt

    String plaintext = transitOperations.decrypt("foo-key", ciphertext);

    System.out.println("Decrypted value");
    System.out.println("-------------------------------");
    System.out.println(plaintext);
    System.out.println("-------------------------------");
    System.out.println();
  }
}

Spring Cloud Vault uses VaultOperations to interact with Vault. Properties from Vault get mapped to MyConfiguration for type-safe access. @EnableConfigurationProperties(MyConfiguration.class) enables configuration property mapping and registers a MyConfiguration bean.

Application includes a main() method that autowires an instance of MyConfiguration.

Build an executable JAR

You can run the application from the command line with Gradle or Maven. You can also build a single executable JAR file that contains all the necessary dependencies, classes, and resources and run that. Building an executable jar makes it easy to ship, version, and deploy the service as an application throughout the development lifecycle, across different environments, and so forth.

If you use Gradle, you can run the application by using ./gradlew bootRun. Alternatively, you can build the JAR file by using ./gradlew build and then run the JAR file, as follows:

java -jar build/libs/gs-accessing-vault-0.1.0.jar

If you use Maven, you can run the application by using ./mvnw spring-boot:run. Alternatively, you can build the JAR file with ./mvnw clean package and then run the JAR file, as follows:

java -jar target/gs-accessing-vault-0.1.0.jar
The steps described here create a runnable JAR. You can also build a classic WAR file.

As our Application implements CommandLineRunner, the run method is invoked automatically when boot starts. You should see something like this:

Value of github.oauth2.key
-------------------------------
foobar
-------------------------------

Encrypted value
-------------------------------
vault:v1:2wgVE2PXiR9o55xbyur5KHJl8IwyGDkDU4l1SZScUq6BuqZYgTopwvc4
-------------------------------

Decrypted value
-------------------------------
Secure message
-------------------------------
Vault’s secret backend compares well to a document store that uses URIs to identify documents. Documents are JSON-based that allows convenient object mapping of Vault data.

Summary

Congratulations! You set up a Vault server and wrote a simple application that uses Spring Vault to read secrets and encrypt data with a strong cipher — all without the headache of implementing key management, a cipher mode, and padding.

Want to write a new guide or contribute to an existing one? Check out our contribution guidelines.

All guides are released with an ASLv2 license for the code, and an Attribution, NoDerivatives creative commons license for the writing.