7

I am investigating how I can use Vault 0.11.1 and Spring Vault 2.0.2.RELEASE. I have set up a dev Vault:

vault server -dev

and added some data

vault kv put secret/certs/jan cert=ABCD

which I can read with Spring Vault

      @Autowired
      private VaultOperations operations;

            String path = "secret/data/certs/jan";
            System.out.println(operations.read(path).getData());

note that, curiously, I have to insert "data/" in the path to find it again.

deleting the data works fine too:

            operations.delete(path);

but writing data with

            Secret secret = new Secret("ABCD");
            operations.write(path, secret);

fails with

org.springframework.vault.VaultException: Status 400 secret/data/certs/jan: no data provided

Secret is a simple bean:

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Secret {
    String cert;
}

and the jason conversion seems to go fine:

DEBUG org.springframework.web.client.RestTemplate - Writing [Secret(cert=ABCD)] using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@add0edd]

Simply trying to write a String

            operations.write(path, "foo=bar");

also fails:

Status 400 secret/data/certs/jan: failed to parse JSON input: invalid character 'o' in literal false (expecting 'a')
Jan Larsen
  • 831
  • 6
  • 13

1 Answers1

18

It looks like you're using Vault's versioned Key-Value backend. New instances of Vault 0.10 and newer mount the versioned backend by default at secret/ which requires you to use a specific API.

There are subtle API differences between the unversioned (v1) and versioned (v2) Key Value backend, that add additional elements to the context path and the actual JSON payload.

Example for Key-Value backend v1:

POST /v1/secret/certs/jan

{"key":"value"}

Example for Key-Value backend v2:

POST /v1/secret/data/certs/jan

{"data":{"key":"value"}}

For using Spring Vault, this means that you need to upgrade to version 2.1.0 and use the VaultKeyValueOperations API:

VaultKeyValueOperations keyValue = vaultOperations.opsForKeyValue("secret", KeyValueBackend.versioned());
keyValue.put("certs/jan", secret);
mp911de
  • 17,546
  • 2
  • 55
  • 95
  • Thanks for the answer. `operations.opsForKeyValue("secret", KeyValueBackend.unversioned()).put("certs/jan", secret)` gets 404 Not Found and `operations.opsForKeyValue("secret", KeyValueBackend.unversioned()).put("data/certs/jan", secret);` gets `VaultException: Status 400 Bad Request: no data provided` – Jan Larsen Oct 05 '18 at 10:17
  • Make sure to use the proper version flag. `KeyValueBackend.unversioned()` for key-value v1 (unversioned) and `KeyValueBackend.versioned()` for key-value v2 (versioned). – mp911de Oct 05 '18 at 10:18
  • `operations.opsForKeyValue("secret", KeyValueBackend.versioned()).put("certs/jan", secret);` works! Thank you. – Jan Larsen Oct 05 '18 at 10:20
  • I was facing the same issue. Adding 'data' resolved it. Thanks – Arvind Kumar Oct 17 '19 at 07:08
  • I tried {"data":{"key":"value"}} = > this is replacing everything in vault. Any way I can just append my new key:value rather than replacing everything with it ? – Ashutosh Tiwari May 24 '21 at 17:55
  • @AshutoshTiwari the path points to a single secret and the data is the content of the secret. If you want to persist the previous content first read, then append to the json, and last write updated secret. You can also have multiple secrets in vault to avoid this, and use different secret path for each of them. – askalski85 Apr 20 '22 at 08:22