Using HashiCorp Vault with Vertx to store secrets

Hello! This post continues in some way the topic of how to get configuration in Vertx apps. We’ve observed general principles, and today we would dive into concrete case study – reading configuration from secured Vault storage. We would talk about how to use Vault server and store data in it, and then how to access this configuration within Vertx app. I encourage you to read my previous post to grab an idea about Vertx Config model. Let start!

Set up a demo project

First, we need to configure a demo project. I use Maven for this. You need to have following dependencies in your pom:

<dependency>
      <groupId>io.vertx</groupId>
      <artifactId>vertx-core</artifactId>
      <version>3.8.0</version>
</dependency>
<dependency>
      <groupId>io.vertx</groupId>
      <artifactId>vertx-config</artifactId>
      <version>3.8.0</version>
</dependency>
<dependency>
      <groupId>io.vertx</groupId>
      <artifactId>vertx-config-vault</artifactId>
      <version>3.8.0</version>
</dependency>

Next step, you need to have a local Vault server installed. If you don’t have your local Vault installation, please refer to this guide. You don’t have to know everything about Vault, but just to have a server on your machine is more than enough. Assert that everything is installed by running in terminal:

$ vault -version

This would return a version of Vault server. By the way, I encourage you to watch the video by Armon Dadgar, who is HashiCorp’s co-founder and CTO, where he explains what is Vault and how it works.

If everything is installed and configured, we can proceed to storing data in Vault and retrieving it as a Vertx configuration.

Store data in Vault

You can to have a development server running by executing command:

$ vault server -dev

This starts a development server:

You need from this root token (we use it for demo but don’t do it in production!) and vault address. Open new terminal window and set these system variables:

$ export VAULT_ADDR='http://127.0.0.1:8200'
$ export VAULT_TOKEN=<root token>

Now we can start saving credentials. Vault uses the key-value structure, and data is stored with path. In this post, let store some tokens, because Vault is often used to store tokens and other secrets. These tokens are not real, but some arbitrary generate UUIDs 🙂 Let store tokens as secret/apis:

$ vault kv put secret/apis mailchimp=565fc736-4373-4ec4-8208-a6b2bd2fea58
$ vault kv get -field=mailchimp secret/apis

In this step we defined a new api token, saved it to Vault storage and retrieved back:

So, we’ve saved our first secret to Vault. Note, that we use an argument field to get a specific value by its key. Without that, we would get all information stored in path. Also, we can save multiple key-value pairs at once:

$ vault kv put secret/apis postmark=3046a850-6530-477a-8672-2d065cbf60d1 dropbox=5792439f-72a1-401f-93a1-9fffe7e9da90 toggl=4d2af408-0f8c-4b6a-96b4-81ee55f65eb6 

$ vault kv get secret/apis

NB! Don’t forget, that new data overrides previous, so now we received version 2. In other words, if we would get all stored tokens, we would not see mailchimp token:

Access Vault configuration

As it was mentioned in my previous post, Vert-x Config library is a programming abstraction, that is used to read configurations from different stores. NB: Vertx-Vault library supports Vault Key/Value version 1 and version 2 engines docs.

Let return to our sample project and write a new verticle:

public class VaultVerticle extends AbstractVerticle {

  @Override
  public void start(Promise<Void> startPromise) throws Exception {

    ConfigStoreOptions storeOptions = new ConfigStoreOptions();
    storeOptions.setType("vault");
    storeOptions.setConfig(new JsonObject().put("host","127.0.0.1")
                           .put("port", 8200)
                           .put("token","s.PY3LiFnfg33aVucV2AnfWaeX")
                           .put("path", "secret/apis"));
    ConfigRetriever configRetriever = ConfigRetriever.create(vertx,
                          new ConfigRetrieverOptions().addStore(storeOptions));

    configRetriever.getConfig(res->{
      if (res.succeeded()){
        System.out.println("Configuration from Vault: ");
        System.out.println(res.result());
        startPromise.complete();
      } else {
        System.out.println("Something was wrong: ");
        System.out.println(res.cause().getLocalizedMessage());
        startPromise.fail(res.cause());
      }
    });

  }

  public static void main(String[] args) {
    Vertx vertx = Vertx.vertx();
    vertx.deployVerticle(new VaultVerticle());
  }
}

What do we do here? Let go step by step by the flow:

  1. We defined ConfigStoreOptions for Vault with Vault server params and token. We use the root token in this example, but don’t do this in production!. When the token is revoked, the access to the secret is blocked. If the token is renewable, the token is renewed when it expires.
  2. We create ConfigRetriever
  3. We get Vault configuration during startup
  4. Print what we received from Vault

Run an app and assert results:

Configuration from Vault:
{"dropbox":"5792439f-72a1-401f-93a1-9fffe7e9da90", "postmark":"3046a850-6530-477a-8672-2d065cbf60d1", "toggl":"4d2af408-0f8c-4b6a-96b4-81ee55f65eb6"}

References

  • Yuri Mednikov. How to get a configuration in Vertx apps with Java (2019), read here

Conclusion

In this quickstart post, we’ve learned how to get Vault configuration from Vertx apps. We had a quick intro to how to store data in Vault and then we saw how to read it from the code. If you want to know more about Vertx configuration model, take a look on my post about Vertx-Config library.

Meanwhile, I encourage you to follow my twitter andreevi_ch to be informed about my new posts. If you have questions, write me message or drop comment below. Have a nice day!

Leave a Reply

Your email address will not be published. Required fields are marked *